diff --git a/.github/workflows/pull_request_full_build.yml b/.github/workflows/pull_request_full_build.yml index e030a42767f7..5274b8db061b 100644 --- a/.github/workflows/pull_request_full_build.yml +++ b/.github/workflows/pull_request_full_build.yml @@ -4,9 +4,10 @@ on: pull_request: branches: - master + - java21 + - nutcracker - 2201.[0-9]+.x - 2201.[0-9]+.[0-9]+-stage - jobs: build-lang: name: Build Ballerina Lang diff --git a/.github/workflows/pull_request_ubuntu_build.yml b/.github/workflows/pull_request_ubuntu_build.yml index 9dc7752bc97b..8d5e95ff9859 100644 --- a/.github/workflows/pull_request_ubuntu_build.yml +++ b/.github/workflows/pull_request_ubuntu_build.yml @@ -15,6 +15,8 @@ on: - native-build - revert-client-decl-master - query-grouping-aggregation + - java21 + - nutcracker jobs: ubuntu_build: diff --git a/.github/workflows/pull_request_windows_build.yml b/.github/workflows/pull_request_windows_build.yml index 6d0ee04e42a7..9aad32f64b00 100644 --- a/.github/workflows/pull_request_windows_build.yml +++ b/.github/workflows/pull_request_windows_build.yml @@ -15,6 +15,9 @@ on: - native-build - revert-client-decl-master - query-grouping-aggregation + - java21 + - nutcracker + jobs: windows_build: name: Build with some tests on Windows diff --git a/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.cast.json b/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.cast.json index a687fab378e1..bcfb9d35a014 100644 --- a/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.cast.json +++ b/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.cast.json @@ -1,15 +1,15 @@ [ { "description": "Define types.", - "code": "type Person record { string name; int age; }; type Employee record { string name; int age; int empNo; }; type Department record { string code; };" + "code": "type PersonOP record { string name; int age; }; type EmployeeOP record { string name; int age; int empNo; }; type DepartmentOP record { string code; };" }, { "description": "Define employee.", - "code": "Employee employee = {name: \"Jane Doe\", age: 25, empNo: 1};" + "code": "EmployeeOP employee = {name: \"Jane Doe\", age: 25, empNo: 1};" }, { "description": "Cas employee to person.", - "code": "Person person = employee;" + "code": "PersonOP person = employee;" }, { "description": "Cas employee to person - get value.", @@ -18,7 +18,7 @@ }, { "description": "Recast back to employee.", - "code": "Employee employeeTwo = person;" + "code": "EmployeeOP employeeTwo = person;" }, { "description": "Recast back to employee - get value.", diff --git a/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.equality.json b/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.equality.json index 75c3e634644d..fe87ea575eef 100644 --- a/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.equality.json +++ b/ballerina-shell/modules/shell-cli/src/test/resources/testcases/operations.equality.json @@ -1,15 +1,15 @@ [ { "description": "Define types.", - "code": "type Employee record { string name; int id; }; type Person record { string name; };" + "code": "type EmployeeEQ record { string name; int id; }; type PersonEQ record { string name; };" }, { "description": "Define employee.", - "code": "final Employee moduleEmployee = {name: \"John\", id: 2102};" + "code": "final EmployeeEQ moduleEmployee = {name: \"John\", id: 2102};" }, { "description": "Define module ref getter.", - "code": "function getModuleEmployee() returns Employee { return moduleEmployee; }" + "code": "function getModuleEmployee() returns EmployeeEQ { return moduleEmployee; }" }, { "description": "Equality ==.", @@ -49,7 +49,7 @@ }, { "description": "Deep inequality in records.", - "code": "Employee e1 = {name: \"Jane\", id: 1100}; Employee e2 = {name: \"Jane\", id: 1100};" + "code": "EmployeeEQ e1 = {name: \"Jane\", id: 1100}; EmployeeEQ e2 = {name: \"Jane\", id: 1100};" }, { "description": "Deep inequality in records. - get value", @@ -58,7 +58,7 @@ }, { "description": "Deep equality in records.", - "code": "Employee e3 = {name: \"Anne\", id: 1100};" + "code": "EmployeeEQ e3 = {name: \"Anne\", id: 1100};" }, { "description": "Deep equality in records. - get value", @@ -67,7 +67,7 @@ }, { "description": "Reference equality ===.", - "code": "Employee e4 = getModuleEmployee(); Person e5 = getModuleEmployee();" + "code": "EmployeeEQ e4 = getModuleEmployee(); PersonEQ e5 = getModuleEmployee();" }, { "description": "Reference equality ===. - get value", diff --git a/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.clone.json b/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.clone.json index 9c13311bedf4..2c6d6586bd66 100644 --- a/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.clone.json +++ b/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.clone.json @@ -1,19 +1,19 @@ [ { "description": "Define types.", - "code": "type Address record { string country; string state; string city; string street; }; type Person record { string name; int age; boolean married; float salary; Address address; };" + "code": "type AddressCloneTest record { string country; string state; string city; string street; }; type PersonCloneTest record { string name; int age; boolean married; float salary; AddressCloneTest address; };" }, { "description": "Define address.", - "code": "Address address = { country: \"USA\", state: \"NC\", city: \"Raleigh\", street: \"Daniels St\" };" + "code": "AddressCloneTest address = { country: \"USA\", state: \"NC\", city: \"Raleigh\", street: \"Daniels St\" };" }, { "description": "Define person.", - "code": "Person person = { name: \"Alex\", age: 24, married: false, salary: 8000.0, address: address };" + "code": "PersonCloneTest person = { name: \"Alex\", age: 24, married: false, salary: 8000.0, address: address };" }, { "description": "Clone operation.", - "code": "Person result = person.clone();" + "code": "PersonCloneTest result = person.clone();" }, { "description": "Check reference equality.", diff --git a/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.immutable.json b/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.immutable.json index 5e1fbfa5298b..870e6107df20 100644 --- a/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.immutable.json +++ b/ballerina-shell/modules/shell-core/src/test/resources/testcases/evaluator/operations.immutable.json @@ -1,11 +1,11 @@ [ { "description": "Define Details.", - "code": "type Details record {| string name; int id; |};" + "code": "type DetailsImmutableTest record {| string name; int id; |};" }, { "description": "Define Student.", - "code": "type Student record {| int 'class; Details details; map marks; |};" + "code": "type StudentImmutableTest record {| int 'class; DetailsImmutableTest details; map marks; |};" }, { "description": "Define addEntryToMap.", @@ -13,11 +13,11 @@ }, { "description": "Define immutable Details", - "code": "Details & readonly immutableDetails = { name: \"May\", id: 112233 };" + "code": "DetailsImmutableTest & readonly immutableDetails = { name: \"May\", id: 112233 };" }, { "description": "Define immutable Student &", - "code": "Student & readonly student = { 'class: 12, details: immutableDetails, marks: { math: 80, physics: 85, chemistry: 75 } };" + "code": "StudentImmutableTest & readonly student = { 'class: 12, details: immutableDetails, marks: { math: 80, physics: 85, chemistry: 75 } };" }, { "description": "Readonly status of student.", diff --git a/bvm/ballerina-rt/build.gradle b/bvm/ballerina-rt/build.gradle index 5d0623d9c4f6..2cd3db732bbe 100644 --- a/bvm/ballerina-rt/build.gradle +++ b/bvm/ballerina-rt/build.gradle @@ -80,6 +80,7 @@ dependencies { dist project(':ballerina-lang:regexp') dist project(':ballerina-lang:jballerina.java') dist project(':ballerina-shell:shell-rt') + dist project(':semtypes') // Third party jars // config diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/constants/RuntimeConstants.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/constants/RuntimeConstants.java index 9dd918efebe1..7b8b06a95715 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/constants/RuntimeConstants.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/constants/RuntimeConstants.java @@ -90,6 +90,8 @@ public final class RuntimeConstants { // Empty value for string public static final BString STRING_EMPTY_VALUE = StringUtils.fromString(""); + public static final Long INT_MAX_VALUE = 9223372036854775807L; + public static final Long INT_MIN_VALUE = -9223372036854775807L - 1L; public static final Integer BBYTE_MIN_VALUE = 0; public static final Integer BBYTE_MAX_VALUE = 255; public static final Integer SIGNED32_MAX_VALUE = 2147483647; diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/creators/TypeCreator.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/creators/TypeCreator.java index b041914300b6..e1a627bbf879 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/creators/TypeCreator.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/creators/TypeCreator.java @@ -50,6 +50,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; /** * Class @{@link TypeCreator} provides APIs to create ballerina type instances. @@ -58,6 +59,7 @@ */ public final class TypeCreator { + private static final RecordTypeCache registeredRecordTypes = new RecordTypeCache(); /** * Creates a new array type with given element type. * @@ -147,7 +149,7 @@ public static TupleType createTupleType(List typeList, Type restType, int * @return the new tuple type */ public static TupleType createTupleType(List typeList, Type restType, - int typeFlags, boolean isCyclic, boolean readonly) { + int typeFlags, boolean isCyclic, boolean readonly) { return new BTupleType(typeList, restType, typeFlags, isCyclic, readonly); } @@ -162,16 +164,16 @@ public static TupleType createTupleType(List typeList, Type restType, * @return the new tuple type */ public static TupleType createTupleType(String name, Module pkg, - int typeFlags, boolean isCyclic, boolean readonly) { + int typeFlags, boolean isCyclic, boolean readonly) { return new BTupleType(name, pkg, typeFlags, isCyclic, readonly); } /** - * Create a {@code MapType} which represents the map type. - * - * @param constraint constraint type which particular map is bound to. - * @return the new map type - */ + * Create a {@code MapType} which represents the map type. + * + * @param constraint constraint type which particular map is bound to. + * @return the new map type + */ public static MapType createMapType(Type constraint) { return new BMapType(constraint); } @@ -224,6 +226,10 @@ public static MapType createMapType(String typeName, Type constraint, Module mod */ public static RecordType createRecordType(String typeName, Module module, long flags, boolean sealed, int typeFlags) { + BRecordType memo = registeredRecordType(typeName, module); + if (memo != null) { + return memo; + } return new BRecordType(typeName, typeName, module, flags, sealed, typeFlags); } @@ -240,8 +246,11 @@ public static RecordType createRecordType(String typeName, Module module, long f * @return the new record type */ public static RecordType createRecordType(String typeName, Module module, long flags, Map fields, - Type restFieldType, - boolean sealed, int typeFlags) { + Type restFieldType, boolean sealed, int typeFlags) { + BRecordType memo = registeredRecordType(typeName, module); + if (memo != null) { + return memo; + } return new BRecordType(typeName, module, flags, fields, restFieldType, sealed, typeFlags); } @@ -520,4 +529,45 @@ public static FiniteType createFiniteType(String typeName, Set values, i private TypeCreator() { } + + private static BRecordType registeredRecordType(String typeName, Module pkg) { + if (typeName == null || pkg == null) { + return null; + } + return registeredRecordTypes.get(new TypeIdentifier(typeName, pkg)); + } + + public static void registerRecordType(BRecordType recordType) { + String name = recordType.getName(); + Module pkg = recordType.getPackage(); + if (name == null || pkg == null) { + return; + } + if (name.contains("$anon")) { + return; + } + TypeIdentifier typeIdentifier = new TypeIdentifier(name, pkg); + registeredRecordTypes.put(typeIdentifier, recordType); + } + + private static final class RecordTypeCache { + + private static final Map cache = new ConcurrentHashMap<>(); + + BRecordType get(TypeIdentifier key) { + return cache.get(key); + } + + void put(TypeIdentifier identifier, BRecordType value) { + cache.put(identifier, value); + } + } + + public record TypeIdentifier(String typeName, Module pkg) { + + public TypeIdentifier { + assert typeName != null; + assert pkg != null; + } + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/PredefinedTypes.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/PredefinedTypes.java index 6541d6aa9811..00cee703124c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/PredefinedTypes.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/PredefinedTypes.java @@ -64,7 +64,7 @@ */ public final class PredefinedTypes { - private static final Module EMPTY_MODULE = new Module(null, null, null); + public static final Module EMPTY_MODULE = new Module(null, null, null); public static final IntegerType TYPE_INT = new BIntegerType(TypeConstants.INT_TNAME, EMPTY_MODULE); public static final IntegerType TYPE_INT_SIGNED_8 = diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/TypeTags.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/TypeTags.java index ebd78439ded5..daf201901bd6 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/TypeTags.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/TypeTags.java @@ -92,10 +92,9 @@ private TypeTags() { } public static boolean isIntegerTypeTag(int tag) { - - // TODO : Fix byte type. Ideally, byte belongs to here. But we have modeled it differently. return switch (tag) { - case INT_TAG, + case BYTE_TAG, + INT_TAG, SIGNED32_INT_TAG, SIGNED16_INT_TAG, SIGNED8_INT_TAG, diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Atom.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Atom.java new file mode 100644 index 000000000000..0ddea623de6c --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Atom.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent the BDD atom. + * + * @since 2201.11.0 + */ +public sealed interface Atom permits RecAtom, TypeAtom { + + /** + * Get the index of the atom. For {@code TypeAtoms} this is a unique index within the {@code Env}. Each + * {@code RecAtom} that points to the same {@code TypeAtom} will have the same index. + * + * @return index of the atom + */ + int index(); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/AtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/AtomicType.java new file mode 100644 index 000000000000..7e5163fa8a78 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/AtomicType.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.FunctionAtomicType; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; + +/** + * Marker type representing AtomicType. + * + * @since 2201.11.0 + */ +public sealed interface AtomicType permits CellAtomicType, FunctionAtomicType, ListAtomicType, MappingAtomicType { + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeBitSet.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeBitSet.java new file mode 100644 index 000000000000..f3fd6f57048b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeBitSet.java @@ -0,0 +1,19 @@ +package io.ballerina.runtime.api.types.semtype; + +abstract sealed class BasicTypeBitSet permits SemType { + + private int all; + + protected BasicTypeBitSet(int all) { + this.all = all; + } + + protected void setAll(int all) { + this.all = all; + } + + public final int all() { + assert all != -1 : "SemType created by no arg constructor must be initialized with setAll"; + return all; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeCode.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeCode.java new file mode 100644 index 000000000000..c9158bce4bf5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BasicTypeCode.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent bit field that indicate which basic type a semType belongs to. + * + * @since 2201.11.0 + */ +public final class BasicTypeCode { + + public static final int CODE_NIL = 0x00; + public static final int CODE_BOOLEAN = 0x01; + public static final int CODE_INT = 0x02; + public static final int CODE_FLOAT = 0x03; + public static final int CODE_DECIMAL = 0x04; + public static final int CODE_STRING = 0x05; + public static final int CODE_ERROR = 0x06; + public static final int CODE_TYPEDESC = 0x07; + public static final int CODE_HANDLE = 0x08; + public static final int CODE_FUNCTION = 0x09; + public static final int CODE_REGEXP = 0x0A; + public static final int CODE_FUTURE = 0x0B; + public static final int CODE_STREAM = 0x0C; + public static final int CODE_LIST = 0x0D; + public static final int CODE_MAPPING = 0x0E; + public static final int CODE_TABLE = 0x0F; + public static final int CODE_XML = 0x10; + public static final int CODE_OBJECT = 0x11; + public static final int CODE_CELL = 0x12; + public static final int CODE_UNDEF = 0x13; + + // Inherently immutable + public static final BasicTypeCode BT_NIL = get(CODE_NIL); + public static final BasicTypeCode BT_BOOLEAN = get(CODE_BOOLEAN); + public static final BasicTypeCode BT_INT = get(CODE_INT); + public static final BasicTypeCode BT_FLOAT = get(CODE_FLOAT); + public static final BasicTypeCode BT_DECIMAL = get(CODE_DECIMAL); + public static final BasicTypeCode BT_STRING = get(CODE_STRING); + public static final BasicTypeCode BT_ERROR = get(CODE_ERROR); + public static final BasicTypeCode BT_TYPEDESC = get(CODE_TYPEDESC); + public static final BasicTypeCode BT_HANDLE = get(CODE_HANDLE); + public static final BasicTypeCode BT_FUNCTION = get(CODE_FUNCTION); + public static final BasicTypeCode BT_REGEXP = get(CODE_REGEXP); + + // Inherently mutable + public static final BasicTypeCode BT_FUTURE = get(CODE_FUTURE); + public static final BasicTypeCode BT_STREAM = get(CODE_STREAM); + + // Selectively immutable + public static final BasicTypeCode BT_LIST = get(CODE_LIST); + public static final BasicTypeCode BT_MAPPING = get(CODE_MAPPING); + public static final BasicTypeCode BT_TABLE = get(CODE_TABLE); + public static final BasicTypeCode BT_XML = get(CODE_XML); + public static final BasicTypeCode BT_OBJECT = get(CODE_OBJECT); + + // Non-val + public static final BasicTypeCode BT_CELL = get(CODE_CELL); + public static final BasicTypeCode BT_UNDEF = get(CODE_UNDEF); + + // Helper bit fields (does not represent basic type tag) + static final int VT_COUNT = CODE_OBJECT + 1; + public static final int BASIC_TYPE_MASK = (1 << (CODE_STRING + 1)) - 1; + public static final int VT_MASK = (1 << VT_COUNT) - 1; + + static final int VT_COUNT_INHERENTLY_IMMUTABLE = CODE_FUTURE; + public static final int VT_INHERENTLY_IMMUTABLE = (1 << VT_COUNT_INHERENTLY_IMMUTABLE) - 1; + + private final int code; + + private BasicTypeCode(int code) { + this.code = code; + } + + public static BasicTypeCode get(int code) { + if (BasicTypeCodeCache.isCached(code)) { + return BasicTypeCodeCache.cache[code]; + } + return new BasicTypeCode(code); + } + + public int code() { + return code; + } + + @Override + public int hashCode() { + return code; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj instanceof BasicTypeCode other) { + return code == other.code; + } + return false; + } + + private static final class BasicTypeCodeCache { + + private static final BasicTypeCode[] cache; + static { + cache = new BasicTypeCode[CODE_UNDEF + 2]; + for (int i = CODE_NIL; i < CODE_UNDEF + 1; i++) { + cache[i] = new BasicTypeCode(i); + } + } + + private static boolean isCached(int code) { + return 0 < code && code < VT_COUNT; + } + + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Bdd.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Bdd.java new file mode 100644 index 000000000000..2573230f05a8 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Bdd.java @@ -0,0 +1,285 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.SubTypeData; + +import static io.ballerina.runtime.api.types.semtype.Conjunction.and; + +/** + * Represent BDD node. Subtypes that uses BDDs to represent subtypes such as + * list, mapping and cell should implement + * their own {@code SubType} implementation that wraps an implementation of this + * class. + * + * @since 2201.11.0 + */ +public abstract sealed class Bdd extends SubType implements SubTypeData permits BddAllOrNothing, BddNode { + + Bdd(boolean all, boolean nothing) { + super(all, nothing); + } + + @Override + public SubType union(SubType other) { + return bddUnion((Bdd) other); + } + + private Bdd bddUnion(Bdd other) { + if (other == this) { + return this; + } else if (this == BddAllOrNothing.ALL || other == BddAllOrNothing.ALL) { + return BddAllOrNothing.ALL; + } else if (other == BddAllOrNothing.NOTHING) { + return this; + } else if (this == BddAllOrNothing.NOTHING) { + return other; + } + BddNode b1Bdd = (BddNode) this; + BddNode b2Bdd = (BddNode) other; + int cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0) { + return bddCreate(b1Bdd.atom(), + b1Bdd.left(), + b1Bdd.middle().bddUnion(other), + b1Bdd.right()); + } else if (cmp > 0) { + return bddCreate(b2Bdd.atom(), + b2Bdd.left(), + this.bddUnion(b2Bdd.middle()), + b2Bdd.right()); + } else { + return bddCreate(b1Bdd.atom(), + b1Bdd.left().bddUnion(b2Bdd.left()), + b1Bdd.middle().bddUnion(b2Bdd.middle()), + b1Bdd.right().bddUnion(b2Bdd.right())); + } + } + + private int atomCmp(Atom a1, Atom a2) { + if (a1 instanceof RecAtom r1) { + if (a2 instanceof RecAtom r2) { + return r1.index() - r2.index(); + } else { + return -1; + } + } else if (a2 instanceof RecAtom) { + return 1; + } else { + return a1.index() - a2.index(); + } + } + + @Override + public SubType intersect(SubType other) { + return bddIntersect((Bdd) other); + } + + private Bdd bddIntersect(Bdd other) { + if (other == this) { + return this; + } else if (this == BddAllOrNothing.NOTHING || other == BddAllOrNothing.NOTHING) { + return BddAllOrNothing.NOTHING; + } else if (other == BddAllOrNothing.ALL) { + return this; + } else if (this == BddAllOrNothing.ALL) { + return other; + } + BddNode b1Bdd = (BddNode) this; + BddNode b2Bdd = (BddNode) other; + int cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0) { + return bddCreate(b1Bdd.atom(), + b1Bdd.left().bddIntersect(other), + b1Bdd.middle().bddIntersect(other), + b1Bdd.right().bddIntersect(other)); + } else if (cmp > 0) { + return bddCreate(b2Bdd.atom(), + this.bddIntersect(b2Bdd.left()), + this.bddIntersect(b2Bdd.middle()), + this.bddIntersect(b2Bdd.right())); + } else { + return bddCreate(b1Bdd.atom(), + b1Bdd.left().bddUnion(b1Bdd.middle()).bddIntersect(b2Bdd.left().bddUnion(b2Bdd.middle())), + BddAllOrNothing.NOTHING, + b1Bdd.right().bddUnion(b1Bdd.middle()).bddIntersect(b2Bdd.right().bddUnion(b2Bdd.middle()))); + } + } + + @Override + public SubType diff(SubType other) { + return bddDiff((Bdd) other); + } + + private Bdd bddDiff(Bdd other) { + if (this == other || other == BddAllOrNothing.ALL || this == BddAllOrNothing.NOTHING) { + return BddAllOrNothing.NOTHING; + } else if (other == BddAllOrNothing.NOTHING) { + return this; + } else if (this == BddAllOrNothing.ALL) { + return other.bddComplement(); + } + BddNode b1Bdd = (BddNode) this; + BddNode b2Bdd = (BddNode) other; + int cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0L) { + return bddCreate(b1Bdd.atom(), + b1Bdd.left().bddUnion(b1Bdd.middle()).bddDiff(other), + BddAllOrNothing.NOTHING, + b1Bdd.right().bddUnion(b1Bdd.middle()).bddDiff(other)); + } else if (cmp > 0L) { + return bddCreate(b2Bdd.atom(), + this.bddDiff(b2Bdd.left().bddUnion(b2Bdd.middle())), + BddAllOrNothing.NOTHING, + this.bddDiff(b2Bdd.right().bddUnion(b2Bdd.middle()))); + } else { + // There is an error in the Castagna paper for this formula. + // The union needs to be materialized here. + // The original formula does not work in a case like (a0|a1) - a0. + // Castagna confirms that the following formula is the correct one. + return bddCreate(b1Bdd.atom(), + b1Bdd.left().bddUnion(b1Bdd.middle()).bddDiff(b2Bdd.left().bddUnion(b2Bdd.middle())), + BddAllOrNothing.NOTHING, + b1Bdd.right().bddUnion(b1Bdd.middle()).bddDiff(b2Bdd.right().bddUnion(b2Bdd.middle()))); + } + } + + @Override + public SubType complement() { + return bddComplement(); + } + + private Bdd bddComplement() { + if (this == BddAllOrNothing.ALL) { + return BddAllOrNothing.NOTHING; + } else if (this == BddAllOrNothing.NOTHING) { + return BddAllOrNothing.ALL; + } + Bdd nothing = BddAllOrNothing.NOTHING; + BddNode b = (BddNode) this; + if (b.right() == nothing) { + return bddCreate(b.atom(), + nothing, + b.left().bddUnion(b.middle()).bddComplement(), + b.middle().bddComplement()); + } else if (b.left() == nothing) { + return bddCreate(b.atom(), + b.middle().bddComplement(), + b.right().bddUnion(b.middle()).bddComplement(), + nothing); + } else if (b.middle() == nothing) { + return bddCreate(b.atom(), + b.left().bddComplement(), + b.left().bddUnion(b.right()).bddComplement(), + b.right().bddComplement()); + } else { + // There is a typo in the Frisch PhD thesis for this formula. + // (It has left and right swapped.) + // Castagna (the PhD supervisor) confirms that this is the correct formula. + return bddCreate(b.atom(), + b.left().bddUnion(b.middle()).bddComplement(), + nothing, + b.right().bddUnion(b.middle()).bddComplement()); + } + } + + private Bdd bddCreate(Atom atom, Bdd left, Bdd middle, Bdd right) { + if (middle == BddAllOrNothing.ALL) { + return middle; + } + if (left.equals(right)) { + return left.bddUnion(right); + } + + return new BddNodeImpl(atom, left, middle, right); + } + + @Override + public boolean isEmpty(Context cx) { + // Basic types that uses Bdd as a delegate should implement isEmpty instead. + throw new IllegalStateException("Bdd don't support isEmpty"); + } + + @Override + public SubTypeData data() { + // Basic types that uses Bdd (and has a meaningful data part) as a delegate should implement data instead. + throw new IllegalStateException("Bdd don't support data"); + } + + public static boolean bddEvery(Context cx, Bdd b, BddPredicate predicate) { + return bddEvery(cx, b, null, null, predicate); + } + + private static boolean bddEvery(Context cx, Bdd b, Conjunction pos, Conjunction neg, BddPredicate predicate) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing == BddAllOrNothing.NOTHING || predicate.apply(cx, pos, neg); + } + BddNode bn = (BddNode) b; + return bddEvery(cx, bn.left(), and(bn.atom(), pos), neg, predicate) + && bddEvery(cx, bn.middle(), pos, neg, predicate) + && bddEvery(cx, bn.right(), pos, and(bn.atom(), neg), predicate); + } + + public static boolean bddEveryPositive(Context cx, Bdd b, Conjunction pos, Conjunction neg, + BddPredicate predicate) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing == BddAllOrNothing.NOTHING || predicate.apply(cx, pos, neg); + } else { + BddNode bn = (BddNode) b; + return bddEveryPositive(cx, bn.left(), andIfPositive(bn.atom(), pos), neg, predicate) + && bddEveryPositive(cx, bn.middle(), pos, neg, predicate) + && bddEveryPositive(cx, bn.right(), pos, andIfPositive(bn.atom(), neg), predicate); + } + } + + private static Conjunction andIfPositive(Atom atom, Conjunction next) { + if (atom instanceof RecAtom recAtom && recAtom.index() < 0) { + return next; + } + return and(atom, next); + } + + public abstract boolean posMaybeEmpty(); + + // This is for debugging purposes. + // It uses the Frisch/Castagna notation. + public static String bddToString(Bdd b, boolean inner) { + if (b instanceof BddAllOrNothing) { + return b.isAll() ? "1" : "0"; + } else { + String str; + BddNode bdd = (BddNode) b; + Atom a = bdd.atom(); + + if (a instanceof RecAtom) { + str = "r"; + } else { + str = "a"; + } + str += a.index(); + str += "?" + bddToString(bdd.left(), true) + ":" + bddToString(bdd.middle(), true) + + ":" + bddToString(bdd.right(), true); + if (inner) { + str = "(" + str + ")"; + } + return str; + } + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddAllOrNothing.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddAllOrNothing.java new file mode 100644 index 000000000000..e5b6c67ebad5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddAllOrNothing.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent the leaf node of a Bdd. + * + * @since 2201.11.0 + */ +public final class BddAllOrNothing extends Bdd { + + public static final BddAllOrNothing ALL = new BddAllOrNothing(true); + public static final BddAllOrNothing NOTHING = new BddAllOrNothing(false); + + private BddAllOrNothing(boolean all) { + super(all, !all); + } + + @Override + public int hashCode() { + return this == ALL ? 1 : 0; + } + + @Override + public boolean equals(Object o) { + return this == o; + } + + @Override + public boolean posMaybeEmpty() { + return this == ALL; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddIsEmptyPredicate.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddIsEmptyPredicate.java new file mode 100644 index 000000000000..d08297a81b06 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddIsEmptyPredicate.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Predicate to check if a BDD is empty. + * + * @since 2201.11.0 + */ +@FunctionalInterface +public interface BddIsEmptyPredicate { + + /** + * Check if the given BDD is empty. + * + * @param cx Type check context + * @param bdd BDD to check + * @return true if the BDD is empty, false otherwise + */ + boolean apply(Context cx, Bdd bdd); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNode.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNode.java new file mode 100644 index 000000000000..001f154701ee --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNode.java @@ -0,0 +1,66 @@ +package io.ballerina.runtime.api.types.semtype; + +/** + * Internal node of a BDD, which represents a disjunction of conjunctions of atoms. + * + * @since 2201.11.0 + */ +public abstract sealed class BddNode extends Bdd permits BddNodeImpl, BddNodeSimple { + + private volatile Integer hashCode = null; + + protected BddNode(boolean all, boolean nothing) { + super(all, nothing); + } + + public static BddNode bddAtom(Atom atom) { + return new BddNodeSimple(atom); + } + + public boolean isSimple() { + return this instanceof BddNodeSimple; + } + + public abstract Atom atom(); + + public abstract Bdd left(); + + public abstract Bdd middle(); + + public abstract Bdd right(); + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof BddNode other)) { + return false; + } + return atom().equals(other.atom()) && left().equals(other.left()) && middle().equals(other.middle()) && + right().equals(other.right()); + } + + @Override + public int hashCode() { + Integer result = hashCode; + if (result == null) { + // No need to synchronize this since {@code computeHashCode} is idempotent + hashCode = result = computeHashCode(); + } + return result; + } + + private int computeHashCode() { + int result = atom().hashCode(); + result = 31 * result + left().hashCode(); + result = 31 * result + middle().hashCode(); + result = 31 * result + right().hashCode(); + return result; + } + + @Override + public boolean posMaybeEmpty() { + return middle().posMaybeEmpty() || right().posMaybeEmpty(); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeImpl.java new file mode 100644 index 000000000000..71b1a9528220 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeImpl.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Generalized implementation of {@code BddNode}. + * + * @since 2201.11.0 + */ +final class BddNodeImpl extends BddNode { + + private final Atom atom; + private final Bdd left; + private final Bdd middle; + private final Bdd right; + + BddNodeImpl(Atom atom, Bdd left, Bdd middle, Bdd right) { + super(false, false); + this.atom = atom; + this.left = left; + this.middle = middle; + this.right = right; + } + + @Override + public Atom atom() { + return atom; + } + + @Override + public Bdd left() { + return left; + } + + @Override + public Bdd middle() { + return middle; + } + + @Override + public Bdd right() { + return right; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeSimple.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeSimple.java new file mode 100644 index 000000000000..bd64cdcf9af5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddNodeSimple.java @@ -0,0 +1,37 @@ +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent a Bdd node that contains a single atom as positive. This is used to reduce the memory overhead of + * BddNodeImpl in representing such nodes + * + * @since 2201.11.0 + */ +final class BddNodeSimple extends BddNode { + + private final Atom atom; + + BddNodeSimple(Atom atom) { + super(false, false); + this.atom = atom; + } + + @Override + public Atom atom() { + return atom; + } + + @Override + public Bdd left() { + return BddAllOrNothing.ALL; + } + + @Override + public Bdd middle() { + return BddAllOrNothing.NOTHING; + } + + @Override + public Bdd right() { + return BddAllOrNothing.NOTHING; + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/FloatSubtype.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java similarity index 59% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/FloatSubtype.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java index 8f28d7bc9025..4a6e47f3c870 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/FloatSubtype.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/BddPredicate.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -14,16 +14,18 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * */ -package io.ballerina.semtype.subtypedata; -import io.ballerina.semtype.ProperSubtypeData; +package io.ballerina.runtime.api.types.semtype; /** - * Represent FloatSubtype. + * Represents a predicate that can be applied to a BDD conjunction. * - * @since 2.0.0 + * @since 2201.11.0 */ -public class FloatSubtype implements ProperSubtypeData { +@FunctionalInterface +public interface BddPredicate { + boolean apply(Context cx, Conjunction posList, Conjunction negList); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Builder.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Builder.java new file mode 100644 index 000000000000..4c4f1149387b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Builder.java @@ -0,0 +1,456 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.BBooleanSubType; +import io.ballerina.runtime.internal.types.semtype.BCellSubType; +import io.ballerina.runtime.internal.types.semtype.BDecimalSubType; +import io.ballerina.runtime.internal.types.semtype.BFloatSubType; +import io.ballerina.runtime.internal.types.semtype.BIntSubType; +import io.ballerina.runtime.internal.types.semtype.BListSubType; +import io.ballerina.runtime.internal.types.semtype.BMappingSubType; +import io.ballerina.runtime.internal.types.semtype.BStringSubType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; +import io.ballerina.runtime.internal.types.semtype.TableUtils; +import io.ballerina.runtime.internal.types.semtype.XmlUtils; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_ERROR; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_FUNCTION; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_FUTURE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_HANDLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_LIST; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_MAPPING; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_OBJECT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_REGEXP; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_TYPEDESC; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_XML; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Core.union; +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; + +/** + * Utility class for creating semtypes. + * + * @since 2201.11.0 + */ +public final class Builder { + + private static final String[] EMPTY_STRING_ARR = new String[0]; + private static final SemType VAL = SemType.from(VT_MASK); + private static final SemType OBJECT = from(BT_OBJECT); + + private static final SemType INNER = getBasicTypeUnion(VAL.all() | from(BasicTypeCode.BT_UNDEF).all()); + private static final SemType ANY = getBasicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code())); + private static final SemType SIMPLE_OR_STRING = + getBasicTypeUnion((1 << BasicTypeCode.BT_NIL.code()) + | (1 << BasicTypeCode.BT_BOOLEAN.code()) + | (1 << BasicTypeCode.BT_INT.code()) + | (1 << BasicTypeCode.BT_FLOAT.code()) + | (1 << BasicTypeCode.BT_DECIMAL.code()) + | (1 << BT_REGEXP.code()) + | (1 << BasicTypeCode.BT_STRING.code())); + + private static final SemType[] EMPTY_TYPES_ARR = new SemType[0]; + + private static final ConcurrentLazySupplier MAPPING_RO = new ConcurrentLazySupplier<>(() -> + basicSubType(BT_MAPPING, BMappingSubType.createDelegate(getBddSubtypeRo())) + ); + private static final ConcurrentLazySupplier ANYDATA = new ConcurrentLazySupplier<>( + () -> { + Env env = Env.getInstance(); + ListDefinition listDef = new ListDefinition(); + MappingDefinition mapDef = new MappingDefinition(); + SemType tableTy = TableUtils.tableContaining(env, mapDef.getSemType(env)); + SemType accum = Stream.of(getSimpleOrStringType(), getXmlType(), listDef.getSemType(env), + mapDef.getSemType(env), + tableTy).reduce(Builder.getNeverType(), Core::union); + listDef.defineListTypeWrapped(env, EMPTY_TYPES_ARR, 0, accum, CELL_MUT_LIMITED); + mapDef.defineMappingTypeWrapped(env, new MappingDefinition.Field[0], accum, CELL_MUT_LIMITED); + return accum; + } + ); + private static final ConcurrentLazySupplier INNER_RO = + new ConcurrentLazySupplier<>(() -> union(getReadonlyType(), getInnerType())); + + private static final ConcurrentLazySupplier LIST_ATOMIC_INNER = + new ConcurrentLazySupplier<>(() -> new ListAtomicType( + FixedLengthArray.empty(), PredefinedTypeEnv.getInstance().cellSemTypeInner())); + private static final ConcurrentLazySupplier MAPPING_ATOMIC_INNER = + new ConcurrentLazySupplier<>(() -> new MappingAtomicType( + EMPTY_STRING_ARR, EMPTY_TYPES_ARR, PredefinedTypeEnv.getInstance().cellSemTypeInner())); + + private static final ConcurrentLazySupplier XML_ELEMENT = new ConcurrentLazySupplier<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_ELEMENT_RO | XmlUtils.XML_PRIMITIVE_ELEMENT_RW)); + private static final ConcurrentLazySupplier XML_COMMENT = new ConcurrentLazySupplier<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_COMMENT_RO | XmlUtils.XML_PRIMITIVE_COMMENT_RW)); + private static final ConcurrentLazySupplier XML_TEXT = new ConcurrentLazySupplier<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_TEXT)); + private static final ConcurrentLazySupplier XML_PI = new ConcurrentLazySupplier<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_PI_RO | XmlUtils.XML_PRIMITIVE_PI_RW)); + private static final ConcurrentLazySupplier XML_NEVER = new ConcurrentLazySupplier<>(() -> + XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_NEVER)); + private static final PredefinedTypeEnv PREDEFINED_TYPE_ENV = PredefinedTypeEnv.getInstance(); + private static final BddNode LIST_SUBTYPE_THREE_ELEMENT = bddAtom(PREDEFINED_TYPE_ENV.atomListThreeElement()); + private static final BddNode LIST_SUBTYPE_THREE_ELEMENT_RO = bddAtom(PREDEFINED_TYPE_ENV.atomListThreeElementRO()); + private static final BddNode LIST_SUBTYPE_TWO_ELEMENT = bddAtom(PREDEFINED_TYPE_ENV.atomListTwoElement()); + + private Builder() { + } + + public static SemType from(BasicTypeCode typeCode) { + if (BasicTypeCache.isCached(typeCode)) { + return BasicTypeCache.cache[typeCode.code()]; + } + return SemType.from(1 << typeCode.code()); + } + + public static SemType getNeverType() { + return SemType.from(0); + } + + public static SemType getNilType() { + return from(BasicTypeCode.BT_NIL); + } + + public static SemType getUndefType() { + return from(BasicTypeCode.BT_UNDEF); + } + + public static SemType getCellType() { + return from(BT_CELL); + } + + public static SemType getInnerType() { + return INNER; + } + + public static SemType getIntType() { + return from(BasicTypeCode.BT_INT); + } + + public static SemType getDecimalType() { + return from(BasicTypeCode.BT_DECIMAL); + } + + public static SemType getFloatType() { + return from(BasicTypeCode.BT_FLOAT); + } + + public static SemType getBooleanType() { + return from(BasicTypeCode.BT_BOOLEAN); + } + + public static SemType getStringType() { + return from(BasicTypeCode.BT_STRING); + } + + public static SemType getCharType() { + return StringTypeCache.charType; + } + + public static SemType getListType() { + return from(BT_LIST); + } + + public static SemType getReadonlyType() { + return PREDEFINED_TYPE_ENV.readonlyType(); + } + + static SemType getBasicTypeUnion(int bitset) { + return switch (bitset) { + case 0 -> getNeverType(); + case VT_MASK -> VAL; + default -> { + if (Integer.bitCount(bitset) == 1) { + int code = Integer.numberOfTrailingZeros(bitset); + // TODO: what are the others? + if (BasicTypeCache.isCached(code)) { + yield BasicTypeCache.cache[code]; + } + } + yield SemType.from(bitset); + } + }; + } + + public static SemType basicSubType(BasicTypeCode basicTypeCode, SubType subType) { + assert !(subType instanceof Bdd) : "BDD should always be wrapped with a delegate"; + assert checkDelegate(basicTypeCode, subType) : "BDd is wrapped in wrong delegate"; + int some = 1 << basicTypeCode.code(); + SubType[] subTypes = initializeSubtypeArray(some); + subTypes[0] = subType; + return SemType.from(0, some, subTypes); + } + + private static boolean checkDelegate(BasicTypeCode basicTypeCode, SubType subType) { + return (basicTypeCode != BT_MAPPING || subType instanceof BMappingSubType) + && (basicTypeCode != BT_LIST || subType instanceof BListSubType) + && (basicTypeCode != BT_CELL || subType instanceof BCellSubType); + } + + public static SemType getIntConst(long value) { + if (value >= IntTypeCache.CACHE_MIN_VALUE && value <= IntTypeCache.CACHE_MAX_VALUE) { + return IntTypeCache.cache[(int) value - IntTypeCache.CACHE_MIN_VALUE]; + } + return createIntSingletonType(value); + } + + private static SemType createIntSingletonType(long value) { + List values = new ArrayList<>(1); + values.add(value); + return basicSubType(BasicTypeCode.BT_INT, BIntSubType.createIntSubType(values)); + } + + public static SemType getBooleanConst(boolean value) { + return value ? BooleanTypeCache.TRUE : BooleanTypeCache.FALSE; + } + + public static SemType createIntRange(long min, long max) { + return basicSubType(BasicTypeCode.BT_INT, BIntSubType.createIntSubType(min, max)); + } + + public static SemType getDecimalConst(BigDecimal value) { + BigDecimal[] values = {value}; + return basicSubType(BasicTypeCode.BT_DECIMAL, BDecimalSubType.createDecimalSubType(true, values)); + } + + public static SemType getFloatConst(double value) { + Double[] values = {value}; + return basicSubType(BasicTypeCode.BT_FLOAT, BFloatSubType.createFloatSubType(true, values)); + } + + // TODO: consider caching small strings + public static SemType getStringConst(String value) { + BStringSubType subType; + String[] values = {value}; + String[] empty = EMPTY_STRING_ARR; + if (value.length() == 1 || value.codePointCount(0, value.length()) == 1) { + subType = BStringSubType.createStringSubType(true, values, true, empty); + } else { + subType = BStringSubType.createStringSubType(true, empty, true, values); + } + return basicSubType(BasicTypeCode.BT_STRING, subType); + } + + static SubType[] initializeSubtypeArray(int some) { + return new SubType[Integer.bitCount(some)]; + } + + public static SemType getRoCellContaining(Env env, SemType ty) { + return getCellContaining(env, ty, CELL_MUT_NONE); + } + + public static SemType getRwCellContaining(Env env, SemType ty) { + return getCellContaining(env, ty, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + } + + public static SemType getCellContaining(Env env, SemType ty, CellAtomicType.CellMutability mut) { + return env.getCachedCellType(ty, mut, () -> createCellSemType(env, ty, mut)); + } + + private static SemType createCellSemType(Env env, SemType ty, CellAtomicType.CellMutability mut) { + CellAtomicType atomicCell = CellAtomicType.from(ty, mut); + TypeAtom atom = env.cellAtom(atomicCell); + BddNode bdd = bddAtom(atom); + return basicSubType(BT_CELL, BCellSubType.createDelegate(bdd)); + } + + public static SemType getValType() { + return getBasicTypeUnion(VT_MASK); + } + + public static SemType getAnyType() { + return ANY; + } + + public static SemType getMappingType() { + return from(BT_MAPPING); + } + + public static SemType getFunctionType() { + return from(BT_FUNCTION); + } + + public static SemType getErrorType() { + return from(BT_ERROR); + } + + public static SemType getXmlType() { + return from(BT_XML); + } + + public static SemType getXmlElementType() { + return XML_ELEMENT.get(); + } + + public static SemType getXmlCommentType() { + return XML_COMMENT.get(); + } + + public static SemType getXmlTextType() { + return XML_TEXT.get(); + } + + public static SemType getXmlNeverType() { + return XML_NEVER.get(); + } + + public static SemType getXmlPIType() { + return XML_PI.get(); + } + + public static SemType getHandleType() { + return from(BT_HANDLE); + } + + public static SemType getFutureType() { + return from(BT_FUTURE); + } + + public static SemType getRegexType() { + return from(BT_REGEXP); + } + + public static SemType getTypeDescType() { + return from(BT_TYPEDESC); + } + + public static SemType getStreamType() { + return from(BasicTypeCode.BT_STREAM); + } + + public static SemType getAnyDataType() { + return ANYDATA.get(); + } + + public static SemType getObjectType() { + return OBJECT; + } + + static SemType mappingRO() { + return MAPPING_RO.get(); + } + + static SemType innerReadOnly() { + return INNER_RO.get(); + } + + static CellAtomicType cellAtomicVal() { + return PREDEFINED_TYPE_ENV.cellAtomicVal(); + } + + public static BddNode getBddSubtypeRo() { + return bddAtom(RecAtom.createRecAtom(0)); + } + + public static ListAtomicType getListAtomicInner() { + return LIST_ATOMIC_INNER.get(); + } + + public static MappingAtomicType getMappingAtomicInner() { + return MAPPING_ATOMIC_INNER.get(); + } + + public static BddNode getListSubtypeThreeElement() { + return LIST_SUBTYPE_THREE_ELEMENT; + } + + public static BddNode getListSubtypeThreeElementRO() { + return LIST_SUBTYPE_THREE_ELEMENT_RO; + } + + public static BddNode getListSubtypeTwoElement() { + return LIST_SUBTYPE_TWO_ELEMENT; + } + + public static SemType getSimpleOrStringType() { + return SIMPLE_OR_STRING; + } + + public static SemType getTableType() { + return from(BasicTypeCode.BT_TABLE); + } + + private static final class IntTypeCache { + + private static final int CACHE_MAX_VALUE = 127; + private static final int CACHE_MIN_VALUE = -128; + private static final SemType[] cache; + static { + cache = new SemType[CACHE_MAX_VALUE - CACHE_MIN_VALUE + 1]; + for (int i = CACHE_MIN_VALUE; i <= CACHE_MAX_VALUE; i++) { + cache[i - CACHE_MIN_VALUE] = createIntSingletonType(i); + } + } + } + + private static final class BooleanTypeCache { + + private static final SemType TRUE = createBooleanSingletonType(true); + private static final SemType FALSE = createBooleanSingletonType(false); + + private static SemType createBooleanSingletonType(boolean value) { + return basicSubType(BasicTypeCode.BT_BOOLEAN, BBooleanSubType.from(value)); + } + } + + private static final class StringTypeCache { + + private static final SemType charType; + static { + BStringSubType subTypeData = BStringSubType.createStringSubType(false, Builder.EMPTY_STRING_ARR, true, + Builder.EMPTY_STRING_ARR); + charType = basicSubType(BasicTypeCode.BT_STRING, subTypeData); + } + } + + private static final class BasicTypeCache { + + private static final SemType[] cache; + static { + cache = new SemType[CODE_UNDEF + 2]; + for (int i = 0; i < CODE_UNDEF + 1; i++) { + cache[i] = SemType.from(1 << i); + } + } + + private static boolean isCached(BasicTypeCode code) { + int i = code.code(); + return 0 < i && i <= CODE_UNDEF; + } + + private static boolean isCached(int code) { + return 0 < code && code <= CODE_UNDEF; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java new file mode 100644 index 000000000000..bad069886c65 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/CacheableTypeDescriptor.java @@ -0,0 +1,41 @@ +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.api.types.Type; + +import java.util.Optional; + +/** + * Represent TypeDescriptors whose type check results can be cached. + * + * @since 2201.11.0 + */ +public interface CacheableTypeDescriptor extends Type { + + /** + * Check whether the type check result of this type descriptor should be cached. Can be used to avoid caching in + * cases where either directly doing the type check is cheaper or we can't determine if two instances of a type + * descriptor are equal without doing a type check. + * + * @return true if the type check result should be cached, false otherwise + */ + boolean shouldCache(); + + /** + * Check whether the type check result of this type descriptor is cached for the given type descriptor. + * + * @param cx Context in which the type check is performed + * @param other Type descriptor to check the cached result for + * @return Optional containing the cached result if it is cached, empty otherwise + */ + Optional cachedTypeCheckResult(Context cx, CacheableTypeDescriptor other); + + /** + * Cache the type check result of this type descriptor for the given type descriptor. Note that implementations of + * this method could choose to not cache the result if {@link #shouldCache()} returns false. In such cases, even + * after calling this method, {@link #cachedTypeCheckResult(Context, CacheableTypeDescriptor)} could return empty. + * + * @param other Type descriptor to cache the result for + * @param result Result of the type check + */ + void cacheTypeCheckResult(CacheableTypeDescriptor other, boolean result); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplier.java new file mode 100644 index 000000000000..2ea49c4906f1 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplier.java @@ -0,0 +1,36 @@ +package io.ballerina.runtime.api.types.semtype; + +import java.util.function.Supplier; + +/** + * A thread-safe single lazy supplier that initialize the value only once. + * + * @param type of the value + * @since 2201.11.0 + */ +public class ConcurrentLazySupplier implements Supplier { + + private Supplier initializer; + private volatile E value = null; + + public ConcurrentLazySupplier(Supplier initializer) { + this.initializer = initializer; + } + + @Override + public E get() { + E result = value; + if (result == null) { + synchronized (this) { + result = value; + if (result == null) { + result = initializer.get(); + assert result != null; + value = result; + initializer = null; + } + } + } + return result; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplierWithCallback.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplierWithCallback.java new file mode 100644 index 000000000000..94646141bb5b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ConcurrentLazySupplierWithCallback.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * A thread-safe lazy supplier that initializes the value on the first call to {@link #get()} and calls the callback. + * + * @param the type of the value + * @since 2201.11.0 + */ +public class ConcurrentLazySupplierWithCallback implements Supplier { + + private volatile E value = null; + private Supplier initializer; + private Consumer callback; + + ConcurrentLazySupplierWithCallback(Supplier initializer, Consumer callback) { + this.initializer = initializer; + this.callback = callback; + } + + @Override + public E get() { + E result = value; + if (result == null) { + synchronized (this) { + result = value; + if (result == null) { + result = initializer.get(); + assert result != null; + value = result; + initializer = null; + callback.accept(result); + callback = null; + } + } + } + return result; + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRoOps.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java similarity index 51% rename from semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRoOps.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java index 88b3ed961a9f..2faf2c559857 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRoOps.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Conjunction.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -14,20 +14,22 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * */ -package io.ballerina.semtype.typeops; -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.TypeCheckContext; +package io.ballerina.runtime.api.types.semtype; /** - * Mapping readonly specific methods operate on SubtypeData. + * Represents the Conjunction in the BDD. * - * @since 2.0.0 + * @param atom Atom of this node + * @param next Next node in the conjunction, will be {@code null} if this is the + * last node + * @since 2201.11.0 */ -public class MappingRoOps extends MappingCommonOps { - @Override - public boolean isEmpty(TypeCheckContext tc, SubtypeData t) { - throw new AssertionError(); +public record Conjunction(Atom atom, Conjunction next) { + + public static Conjunction and(Atom atom, Conjunction next) { + return new Conjunction(atom, next); } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Context.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Context.java new file mode 100644 index 000000000000..4d6634f89f76 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Context.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.BddMemo; +import io.ballerina.runtime.internal.types.semtype.FunctionAtomicType; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.WeakHashMap; + +/** + * Context in which type checking operations are performed. Note context is not + * thread safe, and multiple type check operations should not use the same + * context concurrently. Multiple contexts may share same environment without + * issue. + * + * @since 2201.11.0 + */ +public final class Context { + + // Contains all BddMemo entries with isEmpty == PROVISIONAL + private final List memoStack = new ArrayList<>(); + public final Env env; + public final Map listMemo = new WeakHashMap<>(); + public final Map mappingMemo = new WeakHashMap<>(); + public final Map functionMemo = new WeakHashMap<>(); + private static final int MAX_CACHE_SIZE = 100; + private final Map> typeCheckCacheMemo; + + private Context(Env env) { + this.env = env; + this.typeCheckCacheMemo = createTypeCheckCacheMemo(); + } + + private static Map> createTypeCheckCacheMemo() { + // This is fine since this map is not going to get leaked out of the context and + // context is unique to a thread. So there will be no concurrent modifications + return new LinkedHashMap<>(MAX_CACHE_SIZE, 1f, true) { + @Override + protected boolean removeEldestEntry( + Map.Entry> eldest) { + return size() > MAX_CACHE_SIZE; + } + }; + } + + public static Context from(Env env) { + return new Context(env); + } + + /** + * Memoization logic + * Castagna's paper does not deal with this fully. Although he calls it memoization, it is not, strictly speaking, + * just memoization, since it is not just an optimization, but required for correct handling of recursive types. + * The handling of recursive types depends on our types being defined inductively, rather than coinductively. + * This means that each shape that is a member of the set denoted by the type is finite. There is a tricky problem + * here with memoizing results that rely on assumptions that subsequently turn out to be false. Memoization/caching + * is discussed in section 7.1.2 of the Frisch thesis. This follows Frisch's approach of undoing memoizations that + * turn out to be wrong. (I did not succeed in fully understanding his approach, so I am not completely sure if we + * are doing the same.) + * @param memoTable corresponding memo table for the Bdd + * @param isEmptyPredicate predicate to be applied on the Bdd + * @param bdd Bdd to be checked + * @return result of applying predicate on the bdd + */ + public boolean memoSubtypeIsEmpty(Map memoTable, BddIsEmptyPredicate isEmptyPredicate, Bdd bdd) { + BddMemo m = memoTable.computeIfAbsent(bdd, ignored -> new BddMemo()); + return m.isEmpty().orElseGet(() -> memoSubTypeIsEmptyInner(isEmptyPredicate, bdd, m)); + } + + private boolean memoSubTypeIsEmptyInner(BddIsEmptyPredicate isEmptyPredicate, Bdd bdd, BddMemo m) { + // We are staring the type check with the assumption our type is empty (see: inductive type) + m.isEmpty = BddMemo.Status.PROVISIONAL; + int initStackDepth = memoStack.size(); + memoStack.add(m); + boolean isEmpty = isEmptyPredicate.apply(this, bdd); + boolean isLoop = m.isEmpty == BddMemo.Status.LOOP; + // if not empty our assumption is wrong so we need to reset the memoized values, otherwise we cleanup the stack + // at the end + if (!isEmpty || initStackDepth == 0) { + resetMemoizedValues(initStackDepth, isEmpty, isLoop, m); + } + return isEmpty; + } + + private void resetMemoizedValues(int initStackDepth, boolean isEmpty, boolean isLoop, BddMemo m) { + for (int i = initStackDepth + 1; i < memoStack.size(); i++) { + BddMemo.Status memoStatus = memoStack.get(i).isEmpty; + if (Objects.requireNonNull(memoStatus) == BddMemo.Status.PROVISIONAL || + memoStatus == BddMemo.Status.LOOP || memoStatus == BddMemo.Status.CYCLIC) { + // We started with the assumption our type is empty. Now we know for sure if we are empty or not + // if we are empty all of these who don't have anything except us should be empty as well. + // Otherwise, we don't know if they are empty or not + memoStack.get(i).isEmpty = isEmpty ? BddMemo.Status.TRUE : BddMemo.Status.NULL; + } + } + if (memoStack.size() > initStackDepth) { + memoStack.subList(initStackDepth, memoStack.size()).clear(); + } + if (isLoop && isEmpty) { + // The only way that we have found that this can be empty is by going through a loop. + // This means that the shapes in the type would all be infinite. + // But we define types inductively, which means we only consider finite shapes. + m.isEmpty = BddMemo.Status.CYCLIC; + } else { + m.isEmpty = isEmpty ? BddMemo.Status.TRUE : BddMemo.Status.FALSE; + } + } + + public ListAtomicType listAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecListAtomType(recAtom); + } else { + return (ListAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public MappingAtomicType mappingAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecMappingAtomType(recAtom); + } else { + return (MappingAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public FunctionAtomicType functionAtomicType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecFunctionAtomType(recAtom); + } else { + return (FunctionAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public TypeCheckCache getTypeCheckCache(CacheableTypeDescriptor typeDescriptor) { + return typeCheckCacheMemo.computeIfAbsent(typeDescriptor, TypeCheckCache::new); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java new file mode 100644 index 000000000000..3eefea1b58e4 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Core.java @@ -0,0 +1,520 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.AllOrNothing; +import io.ballerina.runtime.internal.types.semtype.BFutureSubType; +import io.ballerina.runtime.internal.types.semtype.BIntSubType; +import io.ballerina.runtime.internal.types.semtype.BObjectSubType; +import io.ballerina.runtime.internal.types.semtype.BStreamSubType; +import io.ballerina.runtime.internal.types.semtype.BTableSubType; +import io.ballerina.runtime.internal.types.semtype.BTypedescSubType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DelegatedSubType; +import io.ballerina.runtime.internal.types.semtype.EnumerableSubtypeData; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.SubTypeData; +import io.ballerina.runtime.internal.types.semtype.SubtypePair; +import io.ballerina.runtime.internal.types.semtype.SubtypePairs; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Optional; +import java.util.function.Function; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_FLOAT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_INT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_LIST; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_STRING; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_FUTURE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_OBJECT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_STREAM; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_TABLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_TYPEDESC; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; +import static io.ballerina.runtime.api.types.semtype.Builder.getListType; +import static io.ballerina.runtime.api.types.semtype.Builder.getUndefType; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.bddListMemberTypeInnerVal; +import static io.ballerina.runtime.internal.types.semtype.BMappingProj.mappingMemberTypeInner; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.cellAtomType; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.intersectCellAtomicType; + +/** + * Contain functions defined in `core.bal` file. + * + * @since 2201.11.0 + */ +public final class Core { + + private Core() { + } + + public static SemType diff(SemType t1, SemType t2) { + int all1 = t1.all(); + int all2 = t2.all(); + int some1 = t1.some(); + int some2 = t2.some(); + if (some1 == 0) { + if (some2 == 0) { + return Builder.getBasicTypeUnion(all1 & ~all2); + } else { + if (all1 == 0) { + return t1; + } + } + } else { + if (some2 == 0) { + if (all2 == VT_MASK) { + return Builder.getBasicTypeUnion(0); + } + } + } + int all = all1 & ~(all2 | some2); + int some = (all1 | some1) & ~all2; + some = some & ~all; + if (some == 0) { + return SemType.from(all); + } + SubType[] subtypes = Builder.initializeSubtypeArray(some); + int i = 0; + boolean filterNulls = false; + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + SubType data1 = pair.subType1(); + SubType data2 = pair.subType2(); + int code = pair.typeCode(); + SubType data; + if (data1 == null) { + data = data2.complement(); + } else if (data2 == null) { + data = data1; + } else { + data = data1.diff(data2); + } + if (data.isAll()) { + all |= 1 << code; + some &= ~(1 << code); + filterNulls = true; + } else if (data.isNothing()) { + some &= ~(1 << code); + filterNulls = true; + } else { + subtypes[i] = data; + i++; + } + } + return SemType.from(all, some, filterNulls ? filterNulls(some, subtypes) : subtypes); + } + + // TODO: this should return SubTypeData not subtype + public static SubType getComplexSubtypeData(SemType t, BasicTypeCode code) { + assert (t.some() & (1 << code.code())) != 0; + SubType subType = t.subTypeByCode(code.code()); + if (subType instanceof DelegatedSubType wrapper) { + return wrapper.inner(); + } + return subType; + } + + // This computes the spec operation called "member type of K in T", + // for the case when T is a subtype of list, and K is either `int` or a singleton int. + // This is what Castagna calls projection. + // We will extend this to allow `key` to be a SemType, which will turn into an IntSubtype. + // If `t` is not a list, NEVER is returned + public static SemType listMemberTypeInnerVal(Context cx, SemType t, SemType k) { + if (t.some() == 0) { + return (t.all() & getListType().all()) != 0 ? Builder.getValType() : Builder.getNeverType(); + } else { + SubTypeData keyData = intSubtype(k); + if (isNothingSubtype(keyData)) { + return Builder.getNeverType(); + } + return bddListMemberTypeInnerVal(cx, (Bdd) getComplexSubtypeData(t, BT_LIST), keyData, + Builder.getValType()); + } + } + + public static SemType union(SemType t1, SemType t2) { + assert t1 != null && t2 != null; + int all1 = t1.all(); + int some1 = t1.some(); + int all2 = t2.all(); + int some2 = t2.some(); + if (some1 == 0) { + if (some2 == 0) { + return Builder.getBasicTypeUnion(all1 | all2); + } + } + + int all = all1 | all2; + int some = (some1 | some2) & ~all; + if (some == 0) { + return Builder.getBasicTypeUnion(all); + } + SubType[] subtypes = Builder.initializeSubtypeArray(some); + int i = 0; + boolean filterNulls = false; + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + int code = pair.typeCode(); + SubType data1 = pair.subType1(); + SubType data2 = pair.subType2(); + SubType data; + if (data1 == null) { + data = data2; + } else if (data2 == null) { + data = data1; + } else { + data = data1.union(data2); + } + if (data.isAll()) { + filterNulls = true; + all |= 1 << code; + some &= ~(1 << code); + } else { + subtypes[i] = data; + i++; + } + } + if (some == 0) { + return SemType.from(all); + } + return SemType.from(all, some, filterNulls ? filterNulls(some, subtypes) : subtypes); + } + + private static SubType[] filterNulls(int some, SubType[] subtypes) { + int newSize = cardinality(some); + SubType[] filtered = new SubType[newSize]; + System.arraycopy(subtypes, 0, filtered, 0, newSize); + return filtered; + } + + public static SemType intersect(SemType t1, SemType t2) { + assert t1 != null && t2 != null; + int all1 = t1.all(); + int some1 = t1.some(); + int all2 = t2.all(); + int some2 = t2.some(); + if (some1 == 0) { + if (some2 == 0) { + return SemType.from(all1 & all2); + } else { + if (all1 == 0) { + return t1; + } + if (all1 == VT_MASK) { + return t2; + } + } + } else if (some2 == 0) { + if (all2 == 0) { + return t2; + } + if (all2 == VT_MASK) { + return t1; + } + } + + int all = all1 & all2; + int some = (some1 | all1) & (some2 | all2); + some = some & ~all; + if (some == 0) { + return SemType.from(all); + } + + SubType[] subtypes = Builder.initializeSubtypeArray(some); + int i = 0; + boolean filterNulls = false; + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + int code = pair.typeCode(); + SubType data1 = pair.subType1(); + SubType data2 = pair.subType2(); + + SubType data; + if (data1 == null) { + data = data2; + } else if (data2 == null) { + data = data1; + } else { + data = data1.intersect(data2); + } + + if (!data.isNothing()) { + subtypes[i] = data; + i++; + } else { + some &= ~(1 << code); + filterNulls = true; + } + } + if (some == 0) { + return SemType.from(all); + } + return SemType.from(all, some, filterNulls ? filterNulls(some, subtypes) : subtypes); + } + + public static boolean isEmpty(Context cx, SemType t) { + if (t.some() == 0) { + return t.all() == 0; + } + if (t.all() != 0) { + return false; + } + for (SubType subType : t.subTypeData()) { + assert subType != null : "subtype array must not be sparse"; + if (!subType.isEmpty(cx)) { + return false; + } + } + return true; + } + + public static SemType complement(SemType t) { + return diff(Builder.getValType(), t); + } + + public static boolean isNever(SemType t) { + return t.all() == 0 && t.some() == 0; + } + + public static boolean isSubType(Context cx, SemType t1, SemType t2) { + return isEmpty(cx, diff(t1, t2)); + } + + public static boolean isSubtypeSimple(SemType t1, SemType t2) { + assert t1 != null && t2 != null; + int bits = t1.all() | t1.some(); + return (bits & ~t2.all()) == 0; + } + + public static boolean isNothingSubtype(SubTypeData data) { + return data == AllOrNothing.NOTHING; + } + + public static SubTypeData intSubtype(SemType t) { + return subTypeData(t, BT_INT); + } + + public static SubTypeData stringSubtype(SemType t) { + return subTypeData(t, BT_STRING); + } + + public static SubTypeData subTypeData(SemType s, BasicTypeCode code) { + if ((s.all() & (1 << code.code())) != 0) { + return AllOrNothing.ALL; + } + if (s.some() == 0) { + return AllOrNothing.NOTHING; + } + SubType subType = s.subTypeByCode(code.code()); + assert subType != null; + return subType.data(); + } + + public static boolean containsBasicType(SemType t1, SemType t2) { + int bits = t1.all() | t1.some(); + return (bits & t2.all()) != 0; + } + + public static boolean isSameType(Context cx, SemType t1, SemType t2) { + return isSubType(cx, t1, t2) && isSubType(cx, t2, t1); + } + + private static int cardinality(int bitset) { + return Integer.bitCount(bitset); + } + + public static SemType getCellContainingInnerVal(Env env, SemType t) { + CellAtomicType cat = + cellAtomicType(t).orElseThrow(() -> new IllegalArgumentException("t is not a cell semtype")); + return Builder.getCellContaining(env, diff(cat.ty(), getUndefType()), cat.mut()); + } + + public static SemType intersectCellMemberSemTypes(Env env, SemType t1, SemType t2) { + CellAtomicType c1 = + cellAtomicType(t1).orElseThrow(() -> new IllegalArgumentException("t1 is not a cell semtype")); + CellAtomicType c2 = + cellAtomicType(t2).orElseThrow(() -> new IllegalArgumentException("t2 is not a cell semtype")); + + CellAtomicType atomicType = intersectCellAtomicType(c1, c2); + return Builder.getCellContaining(env, atomicType.ty(), + getUndefType().equals(atomicType.ty()) ? CELL_MUT_NONE : atomicType.mut()); + } + + public static Optional cellAtomicType(SemType t) { + SemType cell = Builder.getCellType(); + if (t.some() == 0) { + return cell.equals(t) ? Optional.of(Builder.cellAtomicVal()) : Optional.empty(); + } else { + if (!isSubtypeSimple(t, cell)) { + return Optional.empty(); + } + return bddCellAtomicType((Bdd) getComplexSubtypeData(t, BT_CELL), Builder.cellAtomicVal()); + } + } + + private static Optional bddCellAtomicType(Bdd bdd, CellAtomicType top) { + if (bdd instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + return Optional.of(top); + } + return Optional.empty(); + } + BddNode bddNode = (BddNode) bdd; + return bddNode.isSimple() ? Optional.of(cellAtomType(bddNode.atom())) : Optional.empty(); + } + + public static SemType cellInnerVal(SemType t) { + return diff(cellInner(t), getUndefType()); + } + + public static SemType cellInner(SemType t) { + CellAtomicType cat = + cellAtomicType(t).orElseThrow(() -> new IllegalArgumentException("t is not a cell semtype")); + return cat.ty(); + } + + public static SemType createBasicSemType(BasicTypeCode typeCode, Bdd bdd) { + if (bdd instanceof BddAllOrNothing) { + return bdd.isAll() ? Builder.from(typeCode) : Builder.getNeverType(); + } + SubType subType = switch (typeCode.code()) { + case CODE_OBJECT -> BObjectSubType.createDelegate(bdd); + case CODE_FUTURE -> BFutureSubType.createDelegate(bdd); + case CODE_TYPEDESC -> BTypedescSubType.createDelegate(bdd); + case CODE_TABLE -> BTableSubType.createDelegate(bdd); + case CODE_STREAM -> BStreamSubType.createDelegate(bdd); + default -> throw new IllegalArgumentException("Unexpected type code: " + typeCode); + }; + return SemType.from(0, 1 << typeCode.code(), new SubType[]{subType}); + } + + public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { + return diff(mappingMemberTypeInner(cx, t, k), Builder.getUndefType()); + } + + public static Optional listAtomicType(Context cx, SemType t) { + ListAtomicType listAtomicInner = Builder.getListAtomicInner(); + if (t.some() == 0) { + return Core.isSubtypeSimple(t, Builder.getListType()) ? Optional.ofNullable(listAtomicInner) : + Optional.empty(); + } + Env env = cx.env; + if (!isSubtypeSimple(t, Builder.getListType())) { + return Optional.empty(); + } + return bddListAtomicType(env, (Bdd) getComplexSubtypeData(t, BT_LIST), listAtomicInner); + } + + public static SemType floatToInt(SemType t) { + if (!containsBasicType(t, Builder.getFloatType())) { + return Builder.getNeverType(); + } + return convertEnumerableNumericType(t, BT_FLOAT, Builder.getIntType(), + (floatValue) -> ((Double) floatValue).longValue(), Builder::getIntConst); + } + + public static SemType floatToDecimal(SemType t) { + if (!containsBasicType(t, Builder.getFloatType())) { + return Builder.getNeverType(); + } + return convertEnumerableNumericType(t, BT_FLOAT, Builder.getDecimalType(), + (floatValue) -> BigDecimal.valueOf((Double) floatValue), Builder::getDecimalConst); + } + + public static SemType decimalToInt(SemType t) { + if (!containsBasicType(t, Builder.getDecimalType())) { + return Builder.getNeverType(); + } + return convertEnumerableNumericType(t, BT_DECIMAL, Builder.getIntType(), + (decimalVal) -> ((BigDecimal) decimalVal).longValue(), Builder::getIntConst); + } + + public static SemType decimalToFloat(SemType t) { + if (!containsBasicType(t, Builder.getDecimalType())) { + return Builder.getNeverType(); + } + return convertEnumerableNumericType(t, BT_DECIMAL, Builder.getFloatType(), + (decimalVal) -> ((BigDecimal) decimalVal).doubleValue(), Builder::getFloatConst); + } + + public static SemType intToFloat(SemType t) { + if (!containsBasicType(t, Builder.getIntType())) { + return Builder.getNeverType(); + } + SubTypeData subTypeData = subTypeData(t, BT_INT); + if (subTypeData == AllOrNothing.NOTHING) { + return Builder.getNeverType(); + } + if (subTypeData == AllOrNothing.ALL) { + return Builder.getFloatType(); + } + BIntSubType.IntSubTypeData intSubTypeData = (BIntSubType.IntSubTypeData) subTypeData; + return intSubTypeData.values().stream().map(Builder::getFloatConst).reduce(Builder.getNeverType(), Core::union); + } + + public static SemType intToDecimal(SemType t) { + if (!containsBasicType(t, Builder.getIntType())) { + return Builder.getNeverType(); + } + SubTypeData subTypeData = subTypeData(t, BT_INT); + if (subTypeData == AllOrNothing.NOTHING) { + return Builder.getNeverType(); + } + if (subTypeData == AllOrNothing.ALL) { + return Builder.getDecimalType(); + } + BIntSubType.IntSubTypeData intSubTypeData = (BIntSubType.IntSubTypeData) subTypeData; + return intSubTypeData.values().stream().map(BigDecimal::new).map(Builder::getDecimalConst) + .reduce(Builder.getNeverType(), Core::union); + } + + private static , T extends Comparable> SemType convertEnumerableNumericType( + SemType source, BasicTypeCode targetTypeCode, SemType topType, Function valueConverter, + Function semTypeCreator) { + SubTypeData subTypeData = subTypeData(source, targetTypeCode); + if (subTypeData == AllOrNothing.NOTHING) { + return Builder.getNeverType(); + } + if (subTypeData == AllOrNothing.ALL) { + return topType; + } + //noinspection unchecked - it's a enumerable type + EnumerableSubtypeData enumerableSubtypeData = (EnumerableSubtypeData) subTypeData; + SemType posType = + Arrays.stream(enumerableSubtypeData.values()).map(valueConverter).distinct().map(semTypeCreator) + .reduce(Builder.getNeverType(), Core::union); + if (enumerableSubtypeData.allowed()) { + return posType; + } + return diff(topType, posType); + } + + private static Optional bddListAtomicType(Env env, Bdd bdd, ListAtomicType top) { + if (!(bdd instanceof BddNode bddNode)) { + if (bdd.isAll()) { + return Optional.ofNullable(top); + } else { + return Optional.empty(); + } + } + return bddNode.isSimple() ? Optional.of(env.listAtomType(bddNode.atom())) : Optional.empty(); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java new file mode 100644 index 000000000000..0b0017f05841 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Definition.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; + +/** + * Represent a type definition which will act as a layer of indirection between {@code Env} and the type descriptor. + * + * @since 2201.11.0 + */ +public abstract class Definition { + + private DefinitionContainer container; + + /** + * Get the {@code SemType} of this definition in the given environment. + * + * @param env type environment + */ + public abstract SemType getSemType(Env env); + + /** + * Register the container as the holder of this definition. Used to maintain concurrency invariants. + * + * @param container holder of the definition + * @see io.ballerina.runtime.internal.types.semtype.DefinitionContainer + */ + public void registerContainer(DefinitionContainer container) { + this.container = container; + } + + protected void notifyContainer() { + if (container != null) { + container.definitionUpdated(); + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java new file mode 100644 index 000000000000..10b00639a035 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Env.java @@ -0,0 +1,260 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.FunctionAtomicType; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Supplier; + +/** + * Represent the environment in which {@code SemTypes} are defined in. Type + * checking types defined in different + * environments with each other in undefined. This is safe to be shared between + * multiple threads. + * + * @since 2201.11.0 + */ +public final class Env { + // Currently there is no reason to worry about above restrictions since Env is a singleton, but strictly speaking + // there is not technical restriction preventing multiple instances of Env. + + private static final Env INSTANCE = new Env(); + + // Each atom is created once but will be accessed multiple times during type checking. Also in perfect world we + // will create atoms at the beginning of the execution and will eventually reach + // a steady state. + private final ReadWriteLock atomLock = new ReentrantReadWriteLock(); + private final Map> atomTable; + + private final ReadWriteLock recListLock = new ReentrantReadWriteLock(); + final List recListAtoms; + + private final ReadWriteLock recMapLock = new ReentrantReadWriteLock(); + final List recMappingAtoms; + + private final ReadWriteLock recFunctionLock = new ReentrantReadWriteLock(); + private final List recFunctionAtoms; + + private final Map cellTypeCache = new ConcurrentHashMap<>(); + + private final AtomicInteger distinctAtomCount = new AtomicInteger(0); + + private Env() { + this.atomTable = new WeakHashMap<>(); + this.recListAtoms = new ArrayList<>(); + this.recMappingAtoms = new ArrayList<>(); + this.recFunctionAtoms = new ArrayList<>(); + + PredefinedTypeEnv.getInstance().initializeEnv(this); + } + + public static Env getInstance() { + return INSTANCE; + } + + public TypeAtom cellAtom(CellAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + private TypeAtom typeAtom(AtomicType atomicType) { + atomLock.readLock().lock(); + try { + Reference ref = this.atomTable.get(atomicType); + if (ref != null) { + TypeAtom atom = ref.get(); + if (atom != null) { + return atom; + } + } + } finally { + atomLock.readLock().unlock(); + } + atomLock.writeLock().lock(); + try { + TypeAtom result = TypeAtom.createTypeAtom(this.atomTable.size(), atomicType); + this.atomTable.put(result.atomicType(), new WeakReference<>(result)); + return result; + } finally { + atomLock.writeLock().unlock(); + } + } + + // Ideally this cache should be in the builder as well. But technically we can't cache cells across environments. + // In practice this shouldn't be an issue since there should be only one environment, but I am doing this here + // just in case. + SemType getCachedCellType(SemType ty, CellAtomicType.CellMutability mut, Supplier semTypeCreator) { + if (ty.some() != 0) { + return semTypeCreator.get(); + } + return this.cellTypeCache.computeIfAbsent(new CellSemTypeCacheKey(ty, mut), k -> semTypeCreator.get()); + } + + public RecAtom recListAtom() { + recListLock.writeLock().lock(); + try { + int result = this.recListAtoms.size(); + // represents adding () in nballerina + this.recListAtoms.add(null); + return RecAtom.createRecAtom(result); + } finally { + recListLock.writeLock().unlock(); + } + } + + public void setRecListAtomType(RecAtom rec, ListAtomicType atomicType) { + // NOTE: this is fine since we are not actually changing the recList + recListLock.readLock().lock(); + try { + this.recListAtoms.set(rec.index(), atomicType); + } finally { + recListLock.readLock().unlock(); + } + + } + + public Atom listAtom(ListAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + public ListAtomicType getRecListAtomType(RecAtom ra) { + recListLock.readLock().lock(); + try { + return this.recListAtoms.get(ra.index()); + } finally { + recListLock.readLock().unlock(); + } + } + + public ListAtomicType listAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return getRecListAtomType(recAtom); + } else { + return (ListAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public RecAtom recMappingAtom() { + recMapLock.writeLock().lock(); + try { + int result = this.recMappingAtoms.size(); + // represents adding () in nballerina + this.recMappingAtoms.add(null); + return RecAtom.createRecAtom(result); + } finally { + recMapLock.writeLock().unlock(); + } + } + + public void setRecMappingAtomType(RecAtom rec, MappingAtomicType atomicType) { + recMapLock.readLock().lock(); + try { + this.recMappingAtoms.set(rec.index(), atomicType); + } finally { + recMapLock.readLock().unlock(); + } + } + + public TypeAtom mappingAtom(MappingAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + public MappingAtomicType getRecMappingAtomType(RecAtom recAtom) { + recMapLock.readLock().lock(); + try { + return this.recMappingAtoms.get(recAtom.index()); + } finally { + recMapLock.readLock().unlock(); + } + } + + public RecAtom recFunctionAtom() { + recFunctionLock.writeLock().lock(); + try { + int result = this.recFunctionAtoms.size(); + // represents adding () in nballerina + this.recFunctionAtoms.add(null); + return RecAtom.createRecAtom(result); + } finally { + recFunctionLock.writeLock().unlock(); + } + } + + public void setRecFunctionAtomType(RecAtom rec, FunctionAtomicType atomicType) { + recFunctionLock.readLock().lock(); + try { + this.recFunctionAtoms.set(rec.index(), atomicType); + } finally { + recFunctionLock.readLock().unlock(); + } + } + + public FunctionAtomicType getRecFunctionAtomType(RecAtom recAtom) { + recFunctionLock.readLock().lock(); + try { + return this.recFunctionAtoms.get(recAtom.index()); + } finally { + recFunctionLock.readLock().unlock(); + } + } + + public Atom functionAtom(FunctionAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + private record CellSemTypeCacheKey(SemType ty, CellAtomicType.CellMutability mut) { + + } + + public int distinctAtomCountGetAndIncrement() { + return this.distinctAtomCount.getAndIncrement(); + } + + // This is for debug purposes + public Optional atomicTypeByIndex(int index) { + atomLock.readLock().lock(); + try { + for (Map.Entry> entry : this.atomTable.entrySet()) { + TypeAtom typeAtom = entry.getValue().get(); + if (typeAtom == null) { + continue; + } + if (typeAtom.index() == index) { + return Optional.of(entry.getKey()); + } + } + return Optional.empty(); + } finally { + atomLock.readLock().unlock(); + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPair.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPair.java new file mode 100644 index 000000000000..29ce90a72be5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPair.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent the matching fields types of two mapping atomic types. + * + * @param name name of the field + * @param type1 type of the field in the first mapping + * @param type2 type of the field in teh second mapping + * @param index1 corresponding index of the field in the first mapping. If matching field is rest value is {@code null} + * @param index2 corresponding index of the field in the second mapping. If matching field is rest value is + * {@code null} + * @since 2201.11.0 + */ +public record FieldPair(String name, SemType type1, SemType type2, Integer index1, Integer index2) { + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPairs.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPairs.java new file mode 100644 index 000000000000..82618c4c93f6 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/FieldPairs.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.Common; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; + +/** + * {@code Iterable} over the matching fields of two mapping atomic types. + * + * @since 2201.11.0 + */ +public class FieldPairs implements Iterable { + + MappingAtomicType m1; + MappingAtomicType m2; + private final MappingPairIterator itr; + + public FieldPairs(MappingAtomicType m1, MappingAtomicType m2) { + this.m1 = m1; + this.m2 = m2; + itr = new MappingPairIterator(m1, m2); + } + + @Override + public Iterator iterator() { + return itr; + } + + private static final class MappingPairIterator implements Iterator { + + private final String[] names1; + private final String[] names2; + private final SemType[] types1; + private final SemType[] types2; + private final int len1; + private final int len2; + private int i1 = 0; + private int i2 = 0; + private final SemType rest1; + private final SemType rest2; + + private boolean doneIteration = false; + private boolean shouldCalculate = true; + private FieldPair cache = null; + + private MappingPairIterator(MappingAtomicType m1, MappingAtomicType m2) { + this.names1 = m1.names(); + this.len1 = this.names1.length; + this.types1 = m1.types(); + this.rest1 = m1.rest(); + this.names2 = m2.names(); + this.len2 = this.names2.length; + this.types2 = m2.types(); + this.rest2 = m2.rest(); + } + + @Override + public boolean hasNext() { + if (this.doneIteration) { + return false; + } + if (this.shouldCalculate) { + FieldPair cache = internalNext(); + if (cache == null) { + this.doneIteration = true; + } + this.cache = cache; + this.shouldCalculate = false; + } + return !this.doneIteration; + } + + @Override + public FieldPair next() { + if (this.doneIteration) { + throw new NoSuchElementException("Exhausted iterator"); + } + + if (this.shouldCalculate) { + FieldPair cache = internalNext(); + if (cache == null) { + // this.doneIteration = true; + throw new IllegalStateException(); + } + this.cache = cache; + } + this.shouldCalculate = true; + return this.cache; + } + + /* + * This method corresponds to `next` method of MappingPairing. + */ + private FieldPair internalNext() { + FieldPair p; + if (this.i1 >= this.len1) { + if (this.i2 >= this.len2) { + return null; + } + p = new FieldPair(curName2(), this.rest1, curType2(), null, this.i2); + this.i2 += 1; + } else if (this.i2 >= this.len2) { + p = new FieldPair(curName1(), curType1(), this.rest2, this.i1, null); + this.i1 += 1; + } else { + String name1 = curName1(); + String name2 = curName2(); + if (Common.codePointCompare(name1, name2)) { + p = new FieldPair(name1, curType1(), this.rest2, this.i1, null); + this.i1 += 1; + } else if (Common.codePointCompare(name2, name1)) { + p = new FieldPair(name2, this.rest1, curType2(), null, this.i2); + this.i2 += 1; + } else { + p = new FieldPair(name1, curType1(), curType2(), this.i1, this.i2); + this.i1 += 1; + this.i2 += 1; + } + } + return p; + } + + private SemType curType1() { + return this.types1[this.i1]; + } + + private String curName1() { + return this.names1[this.i1]; + } + + private SemType curType2() { + return this.types2[this.i2]; + } + + private String curName2() { + return this.names2[this.i2]; + } + + public void reset() { + this.i1 = 0; + this.i2 = 0; + } + + public Optional index1(String name) { + int i1Prev = this.i1 - 1; + return i1Prev >= 0 && Objects.equals(this.names1[i1Prev], name) ? Optional.of(i1Prev) : Optional.empty(); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/RecAtom.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java similarity index 54% rename from semtypes/src/main/java/io/ballerina/semtype/RecAtom.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java index eaea5a9a50e5..e1d579e9b266 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/RecAtom.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ListProj.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -15,21 +15,23 @@ * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.BListProj; /** - * Represent a recursive type atom. + * Utility class for list type projection. * - * @since 2.0.0 + * @since 2201.11.0 */ -public class RecAtom implements Atom { - int index; +public final class ListProj { - public RecAtom(int index) { - this.index = index; + private ListProj() { } - public static RecAtom createRecAtom(int index) { - return new RecAtom(index); + public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { + return BListProj.listProjInnerVal(cx, t, k); } + } diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRWOps.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java similarity index 51% rename from semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRWOps.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java index 0a6a69f9c51f..cd3c113054d3 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingRWOps.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/MappingProj.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -14,20 +14,25 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * */ -package io.ballerina.semtype.typeops; -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.TypeCheckContext; +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.BMappingProj; /** - * Mapping read/write specific methods operate on SubtypeData. + * Utility class for mapping type projection. * - * @since 2.0.0 + * @since 2201.11.0 */ -public class MappingRWOps extends MappingCommonOps { - @Override - public boolean isEmpty(TypeCheckContext tc, SubtypeData t) { - throw new AssertionError(); +public final class MappingProj { + + private MappingProj() { } + + public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { + return BMappingProj.mappingMemberTypeInnerVal(cx, t, k); + } + } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Pair.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Pair.java new file mode 100644 index 000000000000..f8d4922c7f56 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/Pair.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Data structure used to pass around pairs of values. + * + * @param type of first value + * @param type of second value + * @param first first values + * @param second second value + * @since 2201.11.0 + */ +public record Pair(E1 first, E2 second) { + + public static Pair from(E1 first, E2 second) { + return new Pair<>(first, second); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java new file mode 100644 index 000000000000..1545a36e572e --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/PredefinedTypeEnv.java @@ -0,0 +1,609 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.BCellSubType; +import io.ballerina.runtime.internal.types.semtype.BListSubType; +import io.ballerina.runtime.internal.types.semtype.BMappingSubType; +import io.ballerina.runtime.internal.types.semtype.BObjectSubType; +import io.ballerina.runtime.internal.types.semtype.BTableSubType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.FixedLengthArray; +import io.ballerina.runtime.internal.types.semtype.ListAtomicType; +import io.ballerina.runtime.internal.types.semtype.MappingAtomicType; +import io.ballerina.runtime.internal.types.semtype.XmlUtils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Supplier; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_CELL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_LIST; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_MAPPING; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_OBJECT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_TABLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_XML; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_INHERENTLY_IMMUTABLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.VT_MASK; +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Builder.basicSubType; +import static io.ballerina.runtime.api.types.semtype.Builder.from; +import static io.ballerina.runtime.api.types.semtype.Builder.getBasicTypeUnion; +import static io.ballerina.runtime.api.types.semtype.Builder.getStringConst; +import static io.ballerina.runtime.api.types.semtype.Core.union; +import static io.ballerina.runtime.api.types.semtype.TypeAtom.createTypeAtom; + +final class PredefinedTypeEnv { + + private static PredefinedTypeEnv instance; + private final AtomicBoolean initialized = new AtomicBoolean(false); + private static final int BDD_REC_ATOM_OBJECT_READONLY = 1; + private static final RecAtom OBJECT_RO_REC_ATOM = RecAtom.createRecAtom(BDD_REC_ATOM_OBJECT_READONLY); + private static final BddNode MAPPING_SUBTYPE_OBJECT_RO = bddAtom(OBJECT_RO_REC_ATOM); + + private final List> initializedCellAtoms = new ArrayList<>(); + private final List> initializedListAtoms = new ArrayList<>(); + private final List> initializedMappingAtoms = new ArrayList<>(); + private final List initializedRecListAtoms = new ArrayList<>(); + private final List initializedRecMappingAtoms = new ArrayList<>(); + private final AtomicInteger nextAtomIndex = new AtomicInteger(0); + // This is to avoid passing down env argument when doing cell type operations. + // Please refer to the cellSubtypeDataEnsureProper() in cell.bal + private final Supplier cellAtomicVal = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(getBasicTypeUnion(VT_MASK), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellVal = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicVal, this::cellAtomIndex); + private final Supplier cellSemTypeVal = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellVal())))); + private final Supplier cellAtomicNever = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(SemType.from(0), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellNever = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicNever, this::cellAtomIndex); + // Represent the typeAtom required to construct equivalent subtypes of map and (any|error)[]. + + private final ConcurrentLazySupplier inner = + new ConcurrentLazySupplier<>(() -> SemType.from(VT_MASK | from(BasicTypeCode.BT_UNDEF).all())); + private final Supplier cellAtomicInner = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(inner.get(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellInner = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInner, this::cellAtomIndex); + private final Supplier cellSemTypeInner = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInner())))); + // TypeAtoms related to (map)[]. This is to avoid passing down env argument when doing + // tableSubtypeComplement operation. + private final Supplier cellAtomicInnerMapping = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(union(Builder.getMappingType(), Builder.getUndefType()), + CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellInnerMapping = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerMapping, this::cellAtomIndex); + private final Supplier listAtomicMapping = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType(FixedLengthArray.empty(), basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerMapping())))), + this::addInitializedListAtom + ); + private final Supplier atomListMapping = + createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMapping, this::listAtomIndex); + // TypeAtoms related to readonly type. This is to avoid requiring context when referring to readonly type. + // CELL_ATOMIC_INNER_MAPPING_RO & LIST_ATOMIC_MAPPING_RO are typeAtoms required to construct + // readonly & (map)[] which is then used for readonly table type when constructing VAL_READONLY + private final Supplier cellAtomicInnerMappingRO = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(union(Builder.mappingRO(), Builder.getUndefType()), + CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellInnerMappingRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerMappingRO, this::cellAtomIndex); + private final Supplier listAtomicMappingRO = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType(FixedLengthArray.empty(), basicSubType( + BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerMappingRO())))), + this::addInitializedListAtom + ); + + private final Supplier listSubtypeMapping = new ConcurrentLazySupplier<>( + () -> bddAtom(atomListMapping.get())); + private final Supplier mappingArray = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_LIST, BListSubType.createDelegate(listSubtypeMapping.get()))); + private final Supplier cellAtomicMappingArray = new ConcurrentLazySupplierWithCallback<>(() -> + CellAtomicType.from(mappingArray.get(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom); + private final Supplier atomCellMappingArray = new ConcurrentLazySupplier<>(() -> { + CellAtomicType cellAtom = cellAtomicMappingArray.get(); + return createTypeAtom(cellAtomIndex(cellAtom), cellAtom); + }); + private final Supplier cellSemTypeListSubtypeMapping = new ConcurrentLazySupplier<>(() -> + basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellMappingArray.get())))); + private final Supplier listAtomicThreeElement = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType( + FixedLengthArray.from(new SemType[]{cellSemTypeListSubtypeMapping.get(), cellSemTypeVal.get()}, 3), + cellSemTypeVal.get()), + this::addInitializedListAtom + ); + private final Supplier atomListThreeElement = new ConcurrentLazySupplier<>(() -> { + ListAtomicType listAtomic = listAtomicThreeElement.get(); + return createTypeAtom(listAtomIndex(listAtomic), listAtomic); + }); + private final Supplier atomListMappingRO = + createTypeAtomSupplierFromCellAtomicSupplier(listAtomicMappingRO, this::listAtomIndex); + + private final Supplier cellAtomicUndef = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(Builder.getUndefType(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom + ); + private final Supplier atomCellUndef = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicUndef, this::cellAtomIndex); + private final Supplier cellSemTypeUndef = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellUndef.get())))); + + private final Supplier listSubtypeMappingRO = new ConcurrentLazySupplier<>(() -> bddAtom( + atomListMappingRO.get())); + private final Supplier mappingArrayRO = new ConcurrentLazySupplier<>(() -> basicSubType( + BT_LIST, BListSubType.createDelegate(listSubtypeMappingRO.get()))); + private final Supplier cellAtomicMappingArrayRO = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(mappingArrayRO.get(), CellAtomicType.CellMutability.CELL_MUT_LIMITED), + this::addInitializedCellAtom + ); + private final Supplier atomCellMappingArrayRO = new ConcurrentLazySupplier<>(() -> { + CellAtomicType cellAtom = cellAtomicMappingArrayRO.get(); + return createTypeAtom(cellAtomIndex(cellAtom), cellAtom); + }); + private final Supplier cellSemTypeListSubtypeMappingRO = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellMappingArrayRO.get())))); + private final Supplier listAtomicThreeElementRO = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType( + FixedLengthArray.from(new SemType[]{cellSemTypeListSubtypeMappingRO.get(), cellSemTypeVal.get()}, + 3), + cellSemTypeUndef.get()), + this::addInitializedListAtom + ); + private final Supplier atomListThreeElementRO = new ConcurrentLazySupplier<>(() -> { + ListAtomicType listAtomic = listAtomicThreeElementRO.get(); + return createTypeAtom(listAtomIndex(listAtomic), listAtomic); + }); + + private final Supplier readonlyType = new ConcurrentLazySupplier<>(() -> unionOf( + SemType.from(VT_INHERENTLY_IMMUTABLE), + basicSubType(BT_LIST, BListSubType.createDelegate(bddSubtypeRo())), + basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddSubtypeRo())), + basicSubType(BT_OBJECT, BObjectSubType.createDelegate(MAPPING_SUBTYPE_OBJECT_RO)), + basicSubType(BT_TABLE, BTableSubType.createDelegate(bddAtom(atomListThreeElementRO.get()))), + basicSubType(BT_XML, XmlUtils.XML_SUBTYPE_RO) + )); + + private final ConcurrentLazySupplier innerReadOnly = + new ConcurrentLazySupplier<>(() -> union(readonlyType.get(), inner.get())); + private final Supplier cellAtomicInnerRO = new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from(innerReadOnly.get(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom + ); + private final Supplier atomCellInnerRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicInnerRO, this::cellAtomIndex); + private final Supplier cellSemTypeInnerRO = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellInnerRO.get())))); + private final Supplier listAtomicRO = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType(FixedLengthArray.empty(), cellSemTypeInnerRO.get()), + this.initializedRecListAtoms::add + ); + private final Supplier mappingAtomicRO = new ConcurrentLazySupplierWithCallback<>( + () -> new MappingAtomicType(new String[]{}, new SemType[]{}, cellSemTypeInnerRO.get()), + initializedRecMappingAtoms::add + ); + // TypeAtoms related to [any|error, any|error]. This is to avoid passing down env argument when doing + // streamSubtypeComplement operation. + private final Supplier cellAtomicObjectMemberKind = + new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from( + union(getStringConst("field"), getStringConst("method")), + CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + private final Supplier atomCellObjectMemberKind = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberKind, this::cellAtomIndex); + private final Supplier cellSemTypeObjectMemberKind = new ConcurrentLazySupplier<>( + () -> Builder.basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberKind())))); + private final Supplier cellAtomicObjectMemberVisibility = + new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from( + union(getStringConst("public"), getStringConst("private")), + CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + private final Supplier atomCellObjectMemberVisibility = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberVisibility, this::cellAtomIndex); + private final Supplier cellSemTypeObjectMemberVisibility = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberVisibility())))); + private final Supplier mappingAtomicObjectMember = new ConcurrentLazySupplierWithCallback<>( + () -> new MappingAtomicType( + new String[]{"kind", "value", "visibility"}, + new SemType[]{cellSemTypeObjectMemberKind.get(), cellSemTypeVal.get(), + cellSemTypeObjectMemberVisibility.get()}, + cellSemTypeUndef.get()), + this::addInitializedMapAtom + ); + private final Supplier atomMappingObjectMember = + createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObjectMember, this::mappingAtomIndex); + private final Supplier mappingSemTypeObjectMember = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMember())))); + private final Supplier cellAtomicObjectMember = + new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from( + mappingSemTypeObjectMember.get(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED), + this::addInitializedCellAtom); + private final Supplier atomCellObjectMember = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMember, this::cellAtomIndex); + private final Supplier cellSemTypeObjectMember = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMember())))); + private final Supplier mappingAtomicObject = new ConcurrentLazySupplierWithCallback<>( + () -> new MappingAtomicType( + new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal.get()}, + cellSemTypeObjectMember.get() + ), + this::addInitializedMapAtom + ); + private final Supplier atomMappingObject = + createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObject, this::mappingAtomIndex); + private final Supplier cellAtomicValRO = + new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from( + readonlyType.get(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + private final Supplier atomCellValRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicValRO, this::cellAtomIndex); + private final Supplier cellSemTypeValRo = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellValRO())))); + private final Supplier mappingAtomicObjectMemberRO = new ConcurrentLazySupplierWithCallback<>( + () -> new MappingAtomicType( + new String[]{"kind", "value", "visibility"}, + new SemType[]{cellSemTypeObjectMemberKind.get(), cellSemTypeValRo.get(), + cellSemTypeObjectMemberVisibility.get()}, + cellSemTypeUndef.get()), + this::addInitializedMapAtom + ); + private final Supplier atomMappingObjectMemberRO = + createTypeAtomSupplierFromCellAtomicSupplier(mappingAtomicObjectMemberRO, this::mappingAtomIndex); + private final Supplier mappingSemTypeObjectMemberRO = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_MAPPING, BMappingSubType.createDelegate(bddAtom(atomMappingObjectMemberRO())))); + private final Supplier cellAtomicObjectMemberRO = + new ConcurrentLazySupplierWithCallback<>( + () -> CellAtomicType.from( + mappingSemTypeObjectMemberRO.get(), CellAtomicType.CellMutability.CELL_MUT_NONE), + this::addInitializedCellAtom); + private final Supplier atomCellObjectMemberRO = + createTypeAtomSupplierFromCellAtomicSupplier(cellAtomicObjectMemberRO, this::cellAtomIndex); + private final Supplier cellSemTypeObjectMemberRO = new ConcurrentLazySupplier<>( + () -> basicSubType(BT_CELL, BCellSubType.createDelegate(bddAtom(atomCellObjectMemberRO())))); + private final Supplier mappingAtomicObjectRO = new ConcurrentLazySupplierWithCallback<>( + () -> new MappingAtomicType( + new String[]{"$qualifiers"}, new SemType[]{cellSemTypeVal.get()}, + cellSemTypeObjectMemberRO.get() + ), + initializedRecMappingAtoms::add + ); + + private final Supplier listAtomicTwoElement = new ConcurrentLazySupplierWithCallback<>( + () -> new ListAtomicType( + FixedLengthArray.from(new SemType[]{cellSemTypeVal.get()}, 2), + cellSemTypeUndef.get()), + this::addInitializedListAtom + ); + private final Supplier atomListTwoElement = new ConcurrentLazySupplier<>(() -> { + ListAtomicType listAtomic = listAtomicTwoElement.get(); + return createTypeAtom(listAtomIndex(listAtomic), listAtomic); + }); + + private PredefinedTypeEnv() { + } + + private static SemType unionOf(SemType... types) { + SemType accum = types[0]; + for (int i = 1; i < types.length; i++) { + accum = union(accum, types[i]); + } + return accum; + } + + private static BddNode bddSubtypeRo() { + return bddAtom(RecAtom.createRecAtom(0)); + } + + + public static synchronized PredefinedTypeEnv getInstance() { + if (instance == null) { + instance = new PredefinedTypeEnv(); + instance.initialize(); + } + return instance; + } + + private static Supplier createTypeAtomSupplierFromCellAtomicSupplier( + Supplier atomicTypeSupplier, IndexSupplier indexSupplier) { + return new ConcurrentLazySupplier<>(() -> { + E atomicType = atomicTypeSupplier.get(); + int index = indexSupplier.get(atomicType); + return createTypeAtom(index, atomicType); + }); + } + + private void initialize() { + // Initialize RecAtoms + mappingAtomicRO(); + listAtomicRO(); + mappingAtomicObjectRO(); + + // initialize atomic types + cellAtomicVal(); + cellAtomicNever(); + cellAtomicInner(); + cellAtomicInnerMapping(); + listAtomicMapping(); + cellAtomicInner(); + listAtomicMappingRO(); + cellAtomicInnerRO(); + initialized.set(true); + } + + private void addInitializedCellAtom(CellAtomicType atom) { + addInitializedAtom(initializedCellAtoms, atom); + } + + private void addInitializedListAtom(ListAtomicType atom) { + addInitializedAtom(initializedListAtoms, atom); + } + + private void addInitializedMapAtom(MappingAtomicType atom) { + addInitializedAtom(initializedMappingAtoms, atom); + } + + private void addInitializedAtom(Collection> atoms, E atom) { + atoms.add(new InitializedTypeAtom<>(atom, nextAtomIndex.getAndIncrement())); + } + + private int cellAtomIndex(CellAtomicType atom) { + return atomIndex(initializedCellAtoms, atom); + } + + private int listAtomIndex(ListAtomicType atom) { + return atomIndex(initializedListAtoms, atom); + } + + private int mappingAtomIndex(MappingAtomicType atom) { + return atomIndex(initializedMappingAtoms, atom); + } + + private int atomIndex(List> initializedAtoms, E atom) { + for (InitializedTypeAtom initializedListAtom : initializedAtoms) { + if (initializedListAtom.atomicType() == atom) { + return initializedListAtom.index(); + } + } + throw new IndexOutOfBoundsException(); + } + + CellAtomicType cellAtomicVal() { + return cellAtomicVal.get(); + } + + TypeAtom atomCellVal() { + return atomCellVal.get(); + } + + CellAtomicType cellAtomicNever() { + return cellAtomicNever.get(); + } + + TypeAtom atomCellNever() { + return atomCellNever.get(); + } + + CellAtomicType cellAtomicInner() { + return cellAtomicInner.get(); + } + + TypeAtom atomCellInner() { + return atomCellInner.get(); + } + + CellAtomicType cellAtomicInnerMapping() { + return cellAtomicInnerMapping.get(); + } + + TypeAtom atomCellInnerMapping() { + return atomCellInnerMapping.get(); + } + + CellAtomicType cellAtomicInnerMappingRO() { + return cellAtomicInnerMappingRO.get(); + } + + TypeAtom atomCellInnerMappingRO() { + return atomCellInnerMappingRO.get(); + } + + ListAtomicType listAtomicMapping() { + return listAtomicMapping.get(); + } + + TypeAtom atomListMapping() { + return atomListMapping.get(); + } + + ListAtomicType listAtomicMappingRO() { + return listAtomicMappingRO.get(); + } + + TypeAtom atomListMappingRO() { + return atomListMappingRO.get(); + } + + CellAtomicType cellAtomicInnerRO() { + return cellAtomicInnerRO.get(); + } + + TypeAtom atomCellInnerRO() { + return atomCellInnerRO.get(); + } + + CellAtomicType cellAtomicUndef() { + return cellAtomicUndef.get(); + } + + TypeAtom atomCellUndef() { + return atomCellUndef.get(); + } + + CellAtomicType cellAtomicValRO() { + return cellAtomicValRO.get(); + } + + TypeAtom atomCellValRO() { + return atomCellValRO.get(); + } + + MappingAtomicType mappingAtomicObjectMemberRO() { + return mappingAtomicObjectMemberRO.get(); + } + + TypeAtom atomMappingObjectMemberRO() { + return atomMappingObjectMemberRO.get(); + } + + CellAtomicType cellAtomicObjectMemberRO() { + return cellAtomicObjectMemberRO.get(); + } + + TypeAtom atomCellObjectMemberRO() { + return atomCellObjectMemberRO.get(); + } + + CellAtomicType cellAtomicObjectMemberKind() { + return cellAtomicObjectMemberKind.get(); + } + + TypeAtom atomCellObjectMemberKind() { + return atomCellObjectMemberKind.get(); + } + + CellAtomicType cellAtomicObjectMemberVisibility() { + return cellAtomicObjectMemberVisibility.get(); + } + + TypeAtom atomCellObjectMemberVisibility() { + return atomCellObjectMemberVisibility.get(); + } + + MappingAtomicType mappingAtomicObjectMember() { + return mappingAtomicObjectMember.get(); + } + + TypeAtom atomMappingObjectMember() { + return atomMappingObjectMember.get(); + } + + CellAtomicType cellAtomicObjectMember() { + return cellAtomicObjectMember.get(); + } + + TypeAtom atomCellObjectMember() { + return atomCellObjectMember.get(); + } + + MappingAtomicType mappingAtomicObject() { + return mappingAtomicObject.get(); + } + + TypeAtom atomMappingObject() { + return atomMappingObject.get(); + } + + ListAtomicType listAtomicRO() { + return listAtomicRO.get(); + } + + MappingAtomicType mappingAtomicRO() { + return mappingAtomicRO.get(); + } + + MappingAtomicType mappingAtomicObjectRO() { + return mappingAtomicObjectRO.get(); + } + + TypeAtom atomListThreeElement() { + return atomListThreeElement.get(); + } + + TypeAtom atomListThreeElementRO() { + return atomListThreeElementRO.get(); + } + + SemType readonlyType() { + return readonlyType.get(); + } + + // Due to some reason SpotBug thinks this method is overrideable if we don't put final here as well. + final void initializeEnv(Env env) { + assert initialized.get() : "PredefinedTypeEnv has not fully initialized, check concurrency issues"; + fillRecAtoms(env.recListAtoms, initializedRecListAtoms); + fillRecAtoms(env.recMappingAtoms, initializedRecMappingAtoms); + initializedCellAtoms.forEach(each -> env.cellAtom(each.atomicType())); + initializedListAtoms.forEach(each -> env.listAtom(each.atomicType())); + } + + private void fillRecAtoms(List envRecAtomList, List initializedRecAtoms) { + int count = reservedRecAtomCount(); + for (int i = 0; i < count; i++) { + if (i < initializedRecAtoms.size()) { + envRecAtomList.add(initializedRecAtoms.get(i)); + } else { + // This is mainly to help with bir serialization/deserialization logic. Given the number of such atoms + // will be small this shouldn't be a problem. + envRecAtomList.add(null); + } + } + } + + private int reservedRecAtomCount() { + return Integer.max(initializedRecListAtoms.size(), initializedRecMappingAtoms.size()); + } + + SemType cellSemTypeInner() { + return cellSemTypeInner.get(); + } + + public Atom atomListTwoElement() { + return atomListTwoElement.get(); + } + + @FunctionalInterface + private interface IndexSupplier { + + int get(E atomicType); + } + + private record InitializedTypeAtom(E atomicType, int index) { + + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/RecAtom.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/RecAtom.java new file mode 100644 index 000000000000..f302a7dc3c45 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/RecAtom.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent a recursive type atom. + * + * @since 2201.11.0 + */ +public final class RecAtom implements Atom { + + private final int index; + private static final int BDD_REC_ATOM_READONLY = 0; + private static final RecAtom ZERO = new RecAtom(BDD_REC_ATOM_READONLY); + + private RecAtom(int index) { + this.index = index; + } + + public static RecAtom createRecAtom(int index) { + if (index == BDD_REC_ATOM_READONLY) { + return ZERO; + } + return new RecAtom(index); + } + + public static RecAtom createDistinctRecAtom(int index) { + return new RecAtom(index); + } + + @Override + public int index() { + return index; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof RecAtom recAtom) { + return recAtom.index == this.index; + } + return false; + } + + @Override + public int hashCode() { + return index; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java new file mode 100644 index 000000000000..d185c96b6c7f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SemType.java @@ -0,0 +1,71 @@ +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; +import io.ballerina.runtime.internal.types.semtype.MutableSemType; +import io.ballerina.runtime.internal.types.semtype.SemTypeHelper; + +/** + * Represent a type in runtime. + * + * @since 2201.11.0 + */ +public sealed class SemType extends BasicTypeBitSet + permits io.ballerina.runtime.internal.types.BType, ImmutableSemType { + + private int some; + private SubType[] subTypeData; + + protected SemType(int all, int some, SubType[] subTypeData) { + super(all); + this.some = some; + this.subTypeData = subTypeData; + } + + protected SemType() { + this(-1, -1, null); + } + + public static SemType from(int all) { + return new SemType(all, 0, null); + } + + public static SemType from(int all, int some, SubType[] subTypes) { + return new SemType(all, some, subTypes); + } + + public final int some() { + assert some != -1 : "SemType created by no arg constructor must be initialized with setSome"; + return some; + } + + public final SubType[] subTypeData() { + return subTypeData; + } + + public final SubType subTypeByCode(int code) { + if ((some() & (1 << code)) == 0) { + return null; + } + int someMask = (1 << code) - 1; + int some = some() & someMask; + return subTypeData()[Integer.bitCount(some)]; + } + + protected void setSome(int some, SubType[] subTypeData) { + this.some = some; + this.subTypeData = subTypeData; + } + + public static SemType tryInto(Type type) { + if (type instanceof MutableSemType mutableSemType) { + mutableSemType.updateInnerSemTypeIfNeeded(); + } + return (SemType) type; + } + + @Override + public String toString() { + return SemTypeHelper.stringRepr(this); + } +} 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 new file mode 100644 index 000000000000..99bc4c9a51f5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/ShapeAnalyzer.java @@ -0,0 +1,72 @@ +package io.ballerina.runtime.api.types.semtype; + +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; + +import java.util.Optional; + +/** + * Utility class for performing shape related operations. + * + * @since 2201.11.0 + */ +public class ShapeAnalyzer { + + private ShapeAnalyzer() { + } + + public static Optional acceptedTypeOf(Context cx, Type typeDesc) { + if (typeDesc instanceof TypeWithAcceptedType typeWithAcceptedType) { + return typeWithAcceptedType.acceptedTypeOf(cx); + } + return Optional.of(SemType.tryInto(typeDesc)); + } + + public static Optional shapeOf(Context cx, Object object) { + if (object == null) { + return Optional.of(Builder.getNilType()); + } else if (object instanceof DecimalValue decimalValue) { + return Optional.of(Builder.getDecimalConst(decimalValue.value())); + } else if (object instanceof Double doubleValue) { + return Optional.of(Builder.getFloatConst(doubleValue)); + } else if (object instanceof Number intValue) { + long value = + intValue instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : intValue.longValue(); + return Optional.of(Builder.getIntConst(value)); + } else if (object instanceof Boolean booleanValue) { + return Optional.of(Builder.getBooleanConst(booleanValue)); + } else if (object instanceof BString stringValue) { + return Optional.of(Builder.getStringConst(stringValue.getValue())); + } else if (object instanceof BValue bValue) { + Type type = bValue.getType(); + if (type instanceof TypeWithShape typeWithShape) { + return typeWithShape.shapeOf(cx, ShapeAnalyzer::shapeOf, object); + } else { + return Optional.empty(); + } + } + return Optional.empty(); + } + + public static Optional inherentTypeOf(Context cx, Object object) { + if (object instanceof BValue bValue) { + return bValue.inherentTypeOf(cx); + } + if (object == null) { + return Optional.of(Builder.getNilType()); + } else if (object instanceof Double doubleValue) { + return Optional.of(Builder.getFloatConst(doubleValue)); + } else if (object instanceof Number intValue) { + long value = + intValue instanceof Byte byteValue ? Byte.toUnsignedLong(byteValue) : intValue.longValue(); + return Optional.of(Builder.getIntConst(value)); + } else if (object instanceof Boolean booleanValue) { + return Optional.of(Builder.getBooleanConst(booleanValue)); + } + return Optional.empty(); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SubType.java new file mode 100644 index 000000000000..85fabfa2303b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/SubType.java @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.internal.types.semtype.SubTypeData; + +import java.util.Objects; + +/** + * Describe set of operation supported by each basic Type. + * + * @since 2201.11.0 + */ +public abstract class SubType { + + private final boolean all; + private final boolean nothing; + + protected SubType(boolean all, boolean nothing) { + this.all = all; + this.nothing = nothing; + } + + public abstract SubType union(SubType other); + + public abstract SubType intersect(SubType other); + + public SubType diff(SubType other) { + return this.intersect(other.complement()); + } + + public abstract SubType complement(); + + public abstract boolean isEmpty(Context cx); + + public final boolean isAll() { + return all; + } + + public final boolean isNothing() { + return nothing; + } + + public abstract SubTypeData data(); + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + SubType other = (SubType) o; + return Objects.equals(data(), other.data()); + } + + @Override + public int hashCode() { + return Objects.hashCode(data()); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeAtom.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeAtom.java new file mode 100644 index 000000000000..02080171937e --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeAtom.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.api.types.semtype; + +/** + * Represent a TypeAtom. Each operand of a type operation could be thought of as + * an atom + * + * @param index unique index within the {@code Env} + * @param atomicType atomic type representing the actual type represented by + * this atom. + * @since 2201.11.0 + */ +public record TypeAtom(int index, AtomicType atomicType) implements Atom { + + public TypeAtom { + assert atomicType != null; + } + + public static TypeAtom createTypeAtom(int index, AtomicType atomicType) { + return new TypeAtom(index, atomicType); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java new file mode 100644 index 000000000000..cf9bc820da2a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/types/semtype/TypeCheckCache.java @@ -0,0 +1,41 @@ +package io.ballerina.runtime.api.types.semtype; + +import io.ballerina.runtime.api.types.Type; + +import java.util.Map; +import java.util.Optional; +import java.util.WeakHashMap; + +/** + * Generalized implementation of type check result cache. It is okay to access + * this from multiple threads but makes no + * guarantee about the consistency of the cache under parallel access. Given + * result don't change due to race conditions + * this should eventually become consistent. + * + * @param Type of the type descriptor which owns this cache + * @since 2201.11.0 + */ +public class TypeCheckCache { + + // Not synchronizing this should be fine since race conditions don't lead to inconsistent results. (i.e. results + // of doing multiple type checks are agnostic to the order of execution). Data races shouldn't lead to tearing in + // 64-bit JVMs. + private final Map cachedResults = new WeakHashMap<>(); + private final T owner; + + public TypeCheckCache(T owner) { + this.owner = owner; + } + + public Optional cachedTypeCheckResult(T other) { + if (other.equals(owner)) { + return Optional.of(true); + } + return Optional.ofNullable(cachedResults.get(other)); + } + + public void cacheTypeCheckResult(T other, boolean result) { + cachedResults.put(other, result); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BCollection.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BCollection.java index a95834a70d6e..1c9d28de48da 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BCollection.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BCollection.java @@ -17,6 +17,8 @@ */ package io.ballerina.runtime.api.values; +import io.ballerina.runtime.api.types.semtype.SemType; + /** *

* {@link BCollection} represents a collection in Ballerina. @@ -32,4 +34,12 @@ public interface BCollection { * @return iterator created. */ BIterator getIterator(); + + default SemType shapeOf() { + return null; + } + + default void cacheShape(SemType semType) { + + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BError.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BError.java index 88b368d827f4..5dd81119cabd 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BError.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BError.java @@ -17,8 +17,14 @@ */ package io.ballerina.runtime.api.values; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.internal.types.TypeWithShape; + import java.io.PrintWriter; import java.util.List; +import java.util.Optional; /** *

@@ -83,4 +89,9 @@ public void printStackTrace(PrintWriter printWriter) { */ public abstract List getCallStack(); + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape type = (TypeWithShape) getType(); + return type.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BRegexpValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BRegexpValue.java index 7de5799b3f45..ed62af17014f 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BRegexpValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BRegexpValue.java @@ -17,8 +17,11 @@ */ package io.ballerina.runtime.api.values; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.RegExpDisjunction; +import java.util.Optional; + /** *

* Represents RegexpValue. @@ -33,4 +36,6 @@ public interface BRegexpValue extends BValue { public RegExpDisjunction getRegExpDisjunction(); BTypedesc getTypedesc(); + + Optional shapeOf(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BString.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BString.java index b7308732d7bc..08d9a54c4e23 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BString.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BString.java @@ -17,6 +17,8 @@ */ package io.ballerina.runtime.api.values; +import io.ballerina.runtime.api.types.Type; + /** * Interface representing ballerina strings. * @@ -40,4 +42,5 @@ public interface BString { BIterator getIterator(); + Type getType(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java index 82a8bdac0a59..c23f563ff6d1 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/api/values/BValue.java @@ -18,8 +18,11 @@ package io.ballerina.runtime.api.values; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; import java.util.Map; +import java.util.Optional; /** *

@@ -58,4 +61,20 @@ default String informalStringValue(BLink parent) { String expressionStringValue(BLink parent); Type getType(); + + /** + * Basic type of the value. + * + * @return {@code SemType} representing the value's basic type + */ + default SemType widenedType() { + // This is wrong since we are actually returning the actual (narrowed) type of the value. But since this is + // used only as an optimization (to avoid recalculating singleton type) in the type checker this is better + // than caching the widened types as well. + return SemType.tryInto(getType()); + } + + default Optional inherentTypeOf(Context cx) { + return Optional.empty(); + } } 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 4705c4ead712..b898315b7878 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 @@ -22,14 +22,19 @@ import io.ballerina.runtime.api.types.ArrayType.ArrayState; import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.FunctionType; -import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.MethodType; -import io.ballerina.runtime.api.types.PredefinedTypes; +import io.ballerina.runtime.api.types.ReadonlyType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; -import io.ballerina.runtime.api.types.UnionType; import io.ballerina.runtime.api.types.XmlNodeType; -import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.CacheableTypeDescriptor; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.values.BDecimal; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BObject; @@ -37,64 +42,39 @@ import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.api.values.BValue; import io.ballerina.runtime.api.values.BXml; -import io.ballerina.runtime.internal.commons.TypeValuePair; import io.ballerina.runtime.internal.types.BAnnotatableType; import io.ballerina.runtime.internal.types.BArrayType; -import io.ballerina.runtime.internal.types.BErrorType; -import io.ballerina.runtime.internal.types.BField; +import io.ballerina.runtime.internal.types.BBooleanType; import io.ballerina.runtime.internal.types.BFiniteType; -import io.ballerina.runtime.internal.types.BFunctionType; -import io.ballerina.runtime.internal.types.BFutureType; import io.ballerina.runtime.internal.types.BIntersectionType; -import io.ballerina.runtime.internal.types.BJsonType; -import io.ballerina.runtime.internal.types.BMapType; -import io.ballerina.runtime.internal.types.BNetworkObjectType; import io.ballerina.runtime.internal.types.BObjectType; -import io.ballerina.runtime.internal.types.BParameterizedType; import io.ballerina.runtime.internal.types.BRecordType; -import io.ballerina.runtime.internal.types.BResourceMethodType; -import io.ballerina.runtime.internal.types.BStreamType; import io.ballerina.runtime.internal.types.BTableType; import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BType; -import io.ballerina.runtime.internal.types.BTypeIdSet; import io.ballerina.runtime.internal.types.BTypeReferenceType; -import io.ballerina.runtime.internal.types.BTypedescType; import io.ballerina.runtime.internal.types.BUnionType; -import io.ballerina.runtime.internal.types.BXmlType; +import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.utils.ErrorUtils; -import io.ballerina.runtime.internal.values.ArrayValue; import io.ballerina.runtime.internal.values.DecimalValue; import io.ballerina.runtime.internal.values.DecimalValueKind; -import io.ballerina.runtime.internal.values.ErrorValue; import io.ballerina.runtime.internal.values.HandleValue; -import io.ballerina.runtime.internal.values.MapValue; -import io.ballerina.runtime.internal.values.MapValueImpl; +import io.ballerina.runtime.internal.values.RefValue; import io.ballerina.runtime.internal.values.RegExpValue; -import io.ballerina.runtime.internal.values.StreamValue; -import io.ballerina.runtime.internal.values.TableValueImpl; -import io.ballerina.runtime.internal.values.TupleValueImpl; import io.ballerina.runtime.internal.values.TypedescValue; import io.ballerina.runtime.internal.values.TypedescValueImpl; import io.ballerina.runtime.internal.values.ValuePair; -import io.ballerina.runtime.internal.values.XmlComment; -import io.ballerina.runtime.internal.values.XmlItem; -import io.ballerina.runtime.internal.values.XmlPi; import io.ballerina.runtime.internal.values.XmlSequence; -import io.ballerina.runtime.internal.values.XmlText; import io.ballerina.runtime.internal.values.XmlValue; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; -import java.util.Map; -import java.util.Objects; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; +import java.util.stream.Stream; import static io.ballerina.runtime.api.constants.RuntimeConstants.BALLERINA_BUILTIN_PKG_PREFIX; import static io.ballerina.runtime.api.constants.RuntimeConstants.BBYTE_MAX_VALUE; @@ -109,8 +89,6 @@ import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED16_MAX_VALUE; import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED32_MAX_VALUE; import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED8_MAX_VALUE; -import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_ANY; -import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_ANYDATA; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_BOOLEAN; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_BYTE; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_DECIMAL; @@ -122,15 +100,8 @@ import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_INT_UNSIGNED_16; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_INT_UNSIGNED_32; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_INT_UNSIGNED_8; -import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_JSON; import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_NULL; -import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_READONLY_JSON; -import static io.ballerina.runtime.api.types.PredefinedTypes.TYPE_STRING; import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; -import static io.ballerina.runtime.api.utils.TypeUtils.isValueType; -import static io.ballerina.runtime.internal.TypeConverter.ERROR_MESSAGE_UNION_END; -import static io.ballerina.runtime.internal.TypeConverter.ERROR_MESSAGE_UNION_SEPARATOR; -import static io.ballerina.runtime.internal.TypeConverter.ERROR_MESSAGE_UNION_START; import static io.ballerina.runtime.internal.utils.CloneUtils.getErrorMessage; /** @@ -143,34 +114,39 @@ public final class TypeChecker { private static final byte MAX_TYPECAST_ERROR_COUNT = 20; private static final String REG_EXP_TYPENAME = "RegExp"; + private static final ThreadLocal threadContext = + ThreadLocal.withInitial(() -> Context.from(Env.getInstance())); public static Object checkCast(Object sourceVal, Type targetType) { List errors = new ArrayList<>(); - Type sourceType = getImpliedType(getType(sourceVal)); - if (checkIsType(errors, sourceVal, sourceType, targetType)) { + if (checkIsType(sourceVal, targetType)) { return sourceVal; } - - if (sourceType.getTag() <= TypeTags.BOOLEAN_TAG && targetType.getTag() <= TypeTags.BOOLEAN_TAG) { - return TypeConverter.castValues(targetType, sourceVal); - } - - // if the source is a numeric value and the target type is a union, try to find a matching - // member. - if (sourceType.getTag() <= TypeTags.BOOLEAN_TAG && targetType.getTag() == TypeTags.UNION_TAG) { - for (Type memberType : ((BUnionType) targetType).getMemberTypes()) { - try { - return TypeConverter.castValues(memberType, sourceVal); - } catch (Exception e) { - //ignore and continue + Type sourceType = getType(sourceVal); + if (Core.containsBasicType(SemType.tryInto(sourceType), ConvertibleCastMaskHolder.CONVERTIBLE_CAST_MASK) && + Core.containsBasicType(SemType.tryInto(targetType), ConvertibleCastMaskHolder.CONVERTIBLE_CAST_MASK)) { + // We need to maintain order for these? + if (targetType instanceof BUnionType unionType) { + for (Type memberType : unionType.getMemberTypes()) { + try { + return TypeConverter.castValues(memberType, sourceVal); + } catch (Exception e) { + //ignore and continue + } } + } else { + return TypeConverter.castValues(targetType, sourceVal); } } - throw createTypeCastError(sourceVal, targetType, errors); } + public static Context context() { + // We are pinning each context to thread. We can't use the same context with multiple type checks concurrently + return threadContext.get(); + } + public static long anyToInt(Object sourceVal) { return TypeConverter.anyToIntCast(sourceVal, () -> ErrorUtils.createTypeCastError(sourceVal, TYPE_INT)); @@ -281,7 +257,14 @@ public static boolean anyToJBoolean(Object sourceVal) { * @return true if the value belongs to the given type, false otherwise */ public static boolean checkIsType(Object sourceVal, Type targetType) { - return checkIsType(null, sourceVal, getType(sourceVal), targetType); + Context cx = context(); + Type sourceType = getType(sourceVal); + if (isSubType(sourceType, targetType)) { + return true; + } + SemType sourceSemType = SemType.tryInto(sourceType); + return couldInherentTypeBeDifferent(sourceSemType) && + isSubTypeWithInherentType(cx, sourceVal, SemType.tryInto(targetType)); } /** @@ -294,22 +277,15 @@ public static boolean checkIsType(Object sourceVal, Type targetType) { * @return true if the value belongs to the given type, false otherwise */ public static boolean checkIsType(List errors, Object sourceVal, Type sourceType, Type targetType) { - if (checkIsType(sourceVal, sourceType, targetType, null)) { - return true; - } - - if (getImpliedType(sourceType).getTag() == TypeTags.XML_TAG && !targetType.isReadOnly()) { - XmlValue val = (XmlValue) sourceVal; - if (val.getNodeType() == XmlNodeType.SEQUENCE) { - return checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), false, null); - } - } + return checkIsType(sourceVal, targetType); + } - if (isMutable(sourceVal, sourceType)) { - return false; + // This is just an optimization since shapes are not cached, when in doubt return false + private static boolean couldInherentTypeBeDifferent(SemType type) { + if (type instanceof TypeWithShape typeWithShape) { + return typeWithShape.couldInherentTypeBeDifferent(); } - - return checkIsLikeOnValue(errors, sourceVal, sourceType, targetType, new ArrayList<>(), false, null); + return true; } /** @@ -332,8 +308,27 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType) { * @return true if the value has the same shape as the given type; false otherwise */ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boolean allowNumericConversion) { - return checkIsLikeType(null, sourceValue, targetType, new ArrayList<>(), allowNumericConversion, - null); + Context cx = context(); + 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); + } + return Core.isSubType(cx, shape, targetSemType); + } + + private static SemType appendNumericConversionTypes(SemType semType) { + SemType result = semType; + // We can represent any int value as a float or a decimal. This is to avoid the overhead of creating + // enumerable semtypes for them + if (Core.containsBasicType(semType, Builder.getIntType())) { + result = Core.union(Core.union(Builder.getDecimalType(), Builder.getFloatType()), result); + } + result = Core.union(result, Core.floatToInt(semType)); + result = Core.union(result, Core.floatToDecimal(semType)); + result = Core.union(result, Core.decimalToInt(semType)); + result = Core.union(result, Core.decimalToFloat(semType)); + return result; } /** @@ -344,29 +339,34 @@ public static boolean checkIsLikeType(Object sourceValue, Type targetType, boole * @return true if the two types are same; false otherwise */ public static boolean isSameType(Type sourceType, Type targetType) { - return sourceType == targetType || sourceType.equals(targetType); + return Core.isSameType(context(), SemType.tryInto(sourceType), SemType.tryInto(targetType)); } public static Type getType(Object value) { - if (value == null) { - return TYPE_NULL; - } else if (value instanceof Number) { - if (value instanceof Long) { - return TYPE_INT; - } else if (value instanceof Double) { - return TYPE_FLOAT; - } else if (value instanceof Integer || value instanceof Byte) { - return TYPE_BYTE; + if (value instanceof BValue bValue) { + if (!(value instanceof BObject bObject)) { + return bValue.getType(); } - } else if (value instanceof BString) { - return TYPE_STRING; - } else if (value instanceof Boolean) { - return TYPE_BOOLEAN; - } else if (value instanceof BObject bObject) { return bObject.getOriginalType(); } + if (value == null) { + return TYPE_NULL; + } else if (value instanceof Number number) { + return getNumberType(number); + } else if (value instanceof Boolean booleanValue) { + return BBooleanType.singletonType(booleanValue); + } + throw new IllegalArgumentException("unexpected value type"); + } - return ((BValue) value).getType(); + private static Type getNumberType(Number number) { + if (number instanceof Double) { + return TYPE_FLOAT; + } + if (number instanceof Integer || number instanceof Byte) { + return TYPE_BYTE; + } + return TYPE_INT; } /** @@ -380,18 +380,6 @@ public static boolean isEqual(Object lhsValue, Object rhsValue) { return isEqual(lhsValue, rhsValue, new HashSet<>()); } - /** - * Check if two decimal values are equal in value. - * - * @param lhsValue The value on the left hand side - * @param rhsValue The value of the right hand side - * @return True if values are equal, else false. - */ - public static boolean checkDecimalEqual(DecimalValue lhsValue, DecimalValue rhsValue) { - return isDecimalRealNumber(lhsValue) && isDecimalRealNumber(rhsValue) && - lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) == 0; - } - /** * Check if two decimal values are exactly equal. * @@ -411,7 +399,7 @@ public static boolean checkDecimalExactEqual(DecimalValue lhsValue, DecimalValue * @param decimalValue The decimal value being checked * @return True if the decimal value is a real number. */ - private static boolean isDecimalRealNumber(DecimalValue decimalValue) { + static boolean isDecimalRealNumber(DecimalValue decimalValue) { return decimalValue.valueKind == DecimalValueKind.ZERO || decimalValue.valueKind == DecimalValueKind.OTHER; } @@ -434,54 +422,36 @@ public static boolean isReferenceEqual(Object lhsValue, Object rhsValue) { return false; } - Type lhsType = getImpliedType(getType(lhsValue)); - Type rhsType = getImpliedType(getType(rhsValue)); - - return switch (lhsType.getTag()) { - case TypeTags.FLOAT_TAG -> { - if (rhsType.getTag() != TypeTags.FLOAT_TAG) { - yield false; - } - yield lhsValue.equals(((Number) rhsValue).doubleValue()); - } - case TypeTags.DECIMAL_TAG -> { - if (rhsType.getTag() != TypeTags.DECIMAL_TAG) { - yield false; - } - yield checkDecimalExactEqual((DecimalValue) lhsValue, (DecimalValue) rhsValue); - } - case TypeTags.INT_TAG, - TypeTags.BYTE_TAG, - TypeTags.BOOLEAN_TAG, - TypeTags.STRING_TAG -> isEqual(lhsValue, rhsValue); - case TypeTags.XML_TAG, - TypeTags.XML_COMMENT_TAG, - TypeTags.XML_ELEMENT_TAG, - TypeTags.XML_PI_TAG, - TypeTags.XML_TEXT_TAG -> { - if (!TypeTags.isXMLTypeTag(rhsType.getTag())) { - yield false; - } - yield isXMLValueRefEqual((XmlValue) lhsValue, (XmlValue) rhsValue); - } - case TypeTags.HANDLE_TAG -> { - if (rhsType.getTag() != TypeTags.HANDLE_TAG) { - yield false; - } - yield isHandleValueRefEqual(lhsValue, rhsValue); - } - case TypeTags.FUNCTION_POINTER_TAG -> lhsType.getPackage().equals(rhsType.getPackage()) && - lhsType.getName().equals(rhsType.getName()) && rhsType.equals(lhsType); - default -> { - if (lhsValue instanceof RegExpValue lhsRegExpValue && rhsValue instanceof RegExpValue) { - yield lhsRegExpValue.equals(rhsValue, new HashSet<>()); - } - yield false; - } - }; + Context cx = context(); + SemType lhsType = widenedType(cx, lhsValue); + SemType rhsType = widenedType(cx, rhsValue); + if (isSimpleBasicSemType(lhsType)) { + return isSimpleBasicValuesEqual(lhsValue, rhsValue); + } + Predicate basicTypePredicate = + (basicType) -> Core.isSubType(cx, lhsType, basicType) && Core.isSubType(cx, rhsType, basicType); + if (basicTypePredicate.test(Builder.getStringType())) { + return isEqual(lhsValue, rhsValue); + } + if (basicTypePredicate.test(Builder.getXmlType())) { + return isXMLValueRefEqual((XmlValue) lhsValue, (XmlValue) rhsValue); + } + if (basicTypePredicate.test(Builder.getHandleType())) { + return isHandleValueRefEqual(lhsValue, rhsValue); + } + if (basicTypePredicate.test(Builder.getFunctionType())) { + return isFunctionPointerEqual(getImpliedType(getType(lhsValue)), getImpliedType(getType(rhsValue))); + } + if (basicTypePredicate.test(Builder.getRegexType())) { + RegExpValue lhsReg = (RegExpValue) lhsValue; + RegExpValue rhsReg = (RegExpValue) rhsValue; + return lhsReg.equals(rhsReg, new HashSet<>()); + } + // Other types have storage identity so == test should have passed + return false; } - private static boolean isXMLValueRefEqual(XmlValue lhsValue, XmlValue rhsValue) { + static boolean isXMLValueRefEqual(XmlValue lhsValue, XmlValue rhsValue) { boolean isLhsXmlSequence = lhsValue.getNodeType() == XmlNodeType.SEQUENCE; boolean isRhsXmlSequence = rhsValue.getNodeType() == XmlNodeType.SEQUENCE; @@ -519,6 +489,38 @@ private static boolean isXMLSequenceRefEqual(XmlSequence lhsValue, XmlSequence r return lhsIter.hasNext() == rhsIter.hasNext(); } + private static boolean isFunctionPointerEqual(Type lhsType, Type rhsType) { + return lhsType.getPackage().equals(rhsType.getPackage()) && + lhsType.getName().equals(rhsType.getName()) && rhsType.equals(lhsType); + } + + private static boolean isSimpleBasicValuesEqual(Object v1, Object v2) { + Context cx = context(); + SemType v1Ty = widenedType(cx, v1); + if (!isSimpleBasicSemType(v1Ty)) { + return false; + } + + SemType v2Ty = widenedType(cx, v2); + if (!isSimpleBasicSemType(v2Ty)) { + return false; + } + + if (!Core.isSameType(cx, v1Ty, v2Ty)) { + return false; + } + + if (Core.isSubType(cx, v1Ty, Builder.getDecimalType())) { + return checkDecimalExactEqual((DecimalValue) v1, (DecimalValue) v2); + } + if (Core.isSubType(cx, v1Ty, Builder.getIntType())) { + Number n1 = (Number) v1; + Number n2 = (Number) v2; + return n1.longValue() == n2.longValue(); + } + return v1.equals(v2); + } + /** * Get the typedesc of a value. * @@ -530,7 +532,7 @@ public static TypedescValue getTypedesc(Object value) { if (type == null) { return null; } - if (isSimpleBasicType(type)) { + if (belongToSingleBasicTypeOrString(type)) { return new TypedescValueImpl(new BFiniteType(value.toString(), Set.of(value), 0)); } if (value instanceof BRefValue bRefValue) { @@ -562,2207 +564,315 @@ public static Object getAnnotValue(TypedescValue typedescValue, BString annotTag * @return flag indicating the equivalence of the two types */ public static boolean checkIsType(Type sourceType, Type targetType) { - return checkIsType(sourceType, targetType, null); + return isSubType(sourceType, targetType); } @Deprecated public static boolean checkIsType(Type sourceType, Type targetType, List unresolvedTypes) { - // First check whether both types are the same. - if (sourceType == targetType || (sourceType.getTag() == targetType.getTag() && sourceType.equals(targetType))) { - return true; - } - - if (checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(sourceType)) { - return true; - } - - if (targetType.isReadOnly() && !sourceType.isReadOnly()) { - return false; - } - - int sourceTypeTag = sourceType.getTag(); - int targetTypeTag = targetType.getTag(); - - switch (sourceTypeTag) { - case TypeTags.INTERSECTION_TAG: - return checkIsType(((BIntersectionType) sourceType).getEffectiveType(), - targetTypeTag != TypeTags.INTERSECTION_TAG ? targetType : - ((BIntersectionType) targetType).getEffectiveType(), unresolvedTypes); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - return checkIsType(((BTypeReferenceType) sourceType).getReferredType(), - targetTypeTag != TypeTags.TYPE_REFERENCED_TYPE_TAG ? targetType : - ((BTypeReferenceType) targetType).getReferredType(), unresolvedTypes); - case TypeTags.PARAMETERIZED_TYPE_TAG: - if (targetTypeTag != TypeTags.PARAMETERIZED_TYPE_TAG) { - return checkIsType(((BParameterizedType) sourceType).getParamValueType(), targetType, - unresolvedTypes); - } - return checkIsType(((BParameterizedType) sourceType).getParamValueType(), - ((BParameterizedType) targetType).getParamValueType(), unresolvedTypes); - case TypeTags.READONLY_TAG: - return checkIsType(PredefinedTypes.ANY_AND_READONLY_OR_ERROR_TYPE, - targetType, unresolvedTypes); - case TypeTags.UNION_TAG: - return isUnionTypeMatch((BUnionType) sourceType, targetType, unresolvedTypes); - case TypeTags.FINITE_TYPE_TAG: - if ((targetTypeTag == TypeTags.FINITE_TYPE_TAG || targetTypeTag <= TypeTags.NULL_TAG || - targetTypeTag == TypeTags.XML_TEXT_TAG)) { - return isFiniteTypeMatch((BFiniteType) sourceType, targetType); - } - break; - default: - break; - } - - return switch (targetTypeTag) { - case TypeTags.BYTE_TAG, - TypeTags.SIGNED8_INT_TAG, - TypeTags.FLOAT_TAG, - TypeTags.DECIMAL_TAG, - TypeTags.CHAR_STRING_TAG, - TypeTags.BOOLEAN_TAG, - TypeTags.NULL_TAG -> sourceTypeTag == targetTypeTag; - case TypeTags.STRING_TAG -> TypeTags.isStringTypeTag(sourceTypeTag); - case TypeTags.XML_TEXT_TAG -> { - if (sourceTypeTag == TypeTags.XML_TAG) { - yield ((BXmlType) sourceType).constraint.getTag() == TypeTags.NEVER_TAG; - } - yield sourceTypeTag == targetTypeTag; - } - case TypeTags.INT_TAG -> sourceTypeTag == TypeTags.INT_TAG || sourceTypeTag == TypeTags.BYTE_TAG || - (sourceTypeTag >= TypeTags.SIGNED8_INT_TAG && sourceTypeTag <= TypeTags.UNSIGNED32_INT_TAG); - case TypeTags.SIGNED16_INT_TAG -> sourceTypeTag == TypeTags.BYTE_TAG || - (sourceTypeTag >= TypeTags.SIGNED8_INT_TAG && sourceTypeTag <= TypeTags.SIGNED16_INT_TAG); - case TypeTags.SIGNED32_INT_TAG -> sourceTypeTag == TypeTags.BYTE_TAG || - (sourceTypeTag >= TypeTags.SIGNED8_INT_TAG && sourceTypeTag <= TypeTags.SIGNED32_INT_TAG); - case TypeTags.UNSIGNED8_INT_TAG -> - sourceTypeTag == TypeTags.BYTE_TAG || sourceTypeTag == TypeTags.UNSIGNED8_INT_TAG; - case TypeTags.UNSIGNED16_INT_TAG -> - sourceTypeTag == TypeTags.BYTE_TAG || sourceTypeTag == TypeTags.UNSIGNED8_INT_TAG || - sourceTypeTag == TypeTags.UNSIGNED16_INT_TAG; - case TypeTags.UNSIGNED32_INT_TAG -> - sourceTypeTag == TypeTags.BYTE_TAG || sourceTypeTag == TypeTags.UNSIGNED8_INT_TAG || - sourceTypeTag == TypeTags.UNSIGNED16_INT_TAG || - sourceTypeTag == TypeTags.UNSIGNED32_INT_TAG; - case TypeTags.ANY_TAG -> checkIsAnyType(sourceType); - case TypeTags.ANYDATA_TAG -> sourceType.isAnydata(); - case TypeTags.SERVICE_TAG -> checkIsServiceType(sourceType, targetType, - unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); - case TypeTags.HANDLE_TAG -> sourceTypeTag == TypeTags.HANDLE_TAG; - case TypeTags.READONLY_TAG -> - checkIsType(sourceType, PredefinedTypes.ANY_AND_READONLY_OR_ERROR_TYPE, unresolvedTypes); - case TypeTags.XML_ELEMENT_TAG, - TypeTags.XML_COMMENT_TAG, - TypeTags.XML_PI_TAG -> targetTypeTag == sourceTypeTag; - case TypeTags.INTERSECTION_TAG -> - checkIsType(sourceType, ((BIntersectionType) targetType).getEffectiveType(), unresolvedTypes); - case TypeTags.TYPE_REFERENCED_TYPE_TAG -> - checkIsType(sourceType, ((BTypeReferenceType) targetType).getReferredType(), unresolvedTypes); - default -> checkIsRecursiveType(sourceType, targetType, - unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); - }; - } - - private static boolean checkIsType(Object sourceVal, Type sourceType, Type targetType, - List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - targetType = getImpliedType(targetType); - - int sourceTypeTag = sourceType.getTag(); - int targetTypeTag = targetType.getTag(); - - // If the source type is neither a record type nor an object type, check `is` type by looking only at the types. - // Else, since records and objects may have `readonly` or `final` fields, need to use the value also. - // e.g., - // const HUNDRED = 100; - // - // type Foo record { - // HUNDRED i; - // }; - // - // type Bar record { - // readonly string|int i; - // }; - // - // where `Bar b = {i: 100};`, `b is Foo` should evaluate to true. - if (sourceTypeTag != TypeTags.RECORD_TYPE_TAG && sourceTypeTag != TypeTags.OBJECT_TYPE_TAG) { - return checkIsType(sourceType, targetType); - } - - if (sourceType == targetType || (sourceType.getTag() == targetType.getTag() && sourceType.equals(targetType))) { - return true; - } - - if (targetType.isReadOnly() && !sourceType.isReadOnly()) { - return false; - } - - return switch (targetTypeTag) { - case TypeTags.ANY_TAG -> checkIsAnyType(sourceType); - case TypeTags.READONLY_TAG -> isInherentlyImmutableType(sourceType) || sourceType.isReadOnly(); - default -> checkIsRecursiveTypeOnValue(sourceVal, sourceType, targetType, sourceTypeTag, targetTypeTag, - unresolvedTypes == null ? new ArrayList<>() : unresolvedTypes); - }; - } - - // Private methods - - private static boolean checkTypeDescType(Type sourceType, BTypedescType targetType, - List unresolvedTypes) { - if (sourceType.getTag() != TypeTags.TYPEDESC_TAG) { - return false; - } - - BTypedescType sourceTypedesc = (BTypedescType) sourceType; - return checkIsType(sourceTypedesc.getConstraint(), targetType.getConstraint(), unresolvedTypes); - } - - private static boolean checkIsRecursiveType(Type sourceType, Type targetType, List unresolvedTypes) { - return switch (targetType.getTag()) { - case TypeTags.MAP_TAG -> checkIsMapType(sourceType, (BMapType) targetType, unresolvedTypes); - case TypeTags.STREAM_TAG -> checkIsStreamType(sourceType, (BStreamType) targetType, unresolvedTypes); - case TypeTags.TABLE_TAG -> checkIsTableType(sourceType, (BTableType) targetType, unresolvedTypes); - case TypeTags.JSON_TAG -> checkIsJSONType(sourceType, unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG -> checkIsRecordType(sourceType, (BRecordType) targetType, unresolvedTypes); - case TypeTags.FUNCTION_POINTER_TAG -> checkIsFunctionType(sourceType, (BFunctionType) targetType); - case TypeTags.ARRAY_TAG -> checkIsArrayType(sourceType, (BArrayType) targetType, unresolvedTypes); - case TypeTags.TUPLE_TAG -> checkIsTupleType(sourceType, (BTupleType) targetType, unresolvedTypes); - case TypeTags.UNION_TAG -> checkIsUnionType(sourceType, (BUnionType) targetType, unresolvedTypes); - case TypeTags.OBJECT_TYPE_TAG -> - checkObjectEquivalency(sourceType, (BObjectType) targetType, unresolvedTypes); - case TypeTags.FINITE_TYPE_TAG -> checkIsFiniteType(sourceType, (BFiniteType) targetType); - case TypeTags.FUTURE_TAG -> checkIsFutureType(sourceType, (BFutureType) targetType, unresolvedTypes); - case TypeTags.ERROR_TAG -> checkIsErrorType(sourceType, (BErrorType) targetType, unresolvedTypes); - case TypeTags.TYPEDESC_TAG -> checkTypeDescType(sourceType, (BTypedescType) targetType, unresolvedTypes); - case TypeTags.XML_TAG -> checkIsXMLType(sourceType, targetType, unresolvedTypes); - // other non-recursive types shouldn't reach here - default -> false; - }; - } - - private static boolean checkIsRecursiveTypeOnValue(Object sourceVal, Type sourceType, Type targetType, - int sourceTypeTag, int targetTypeTag, - List unresolvedTypes) { - return switch (targetTypeTag) { - case TypeTags.ANYDATA_TAG -> { - if (sourceTypeTag == TypeTags.OBJECT_TYPE_TAG) { - yield false; - } - yield checkRecordBelongsToAnydataType((MapValue) sourceVal, (BRecordType) sourceType, unresolvedTypes); - } - case TypeTags.MAP_TAG -> checkIsMapType(sourceVal, sourceType, (BMapType) targetType, unresolvedTypes); - case TypeTags.JSON_TAG -> checkIsMapType(sourceVal, sourceType, - new BMapType(targetType.isReadOnly() ? TYPE_READONLY_JSON : - TYPE_JSON), unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG -> - checkIsRecordType(sourceVal, sourceType, (BRecordType) targetType, unresolvedTypes); - case TypeTags.UNION_TAG -> { - for (Type type : ((BUnionType) targetType).getMemberTypes()) { - if (checkIsType(sourceVal, sourceType, type, unresolvedTypes)) { - yield true; - } - } - yield false; - } - case TypeTags.OBJECT_TYPE_TAG -> - checkObjectEquivalency(sourceVal, sourceType, (BObjectType) targetType, unresolvedTypes); - default -> false; - }; - } - - private static boolean isFiniteTypeMatch(BFiniteType sourceType, Type targetType) { - for (Object bValue : sourceType.valueSpace) { - if (!checkIsType(bValue, targetType)) { - return false; - } - } - return true; + return isSubType(sourceType, targetType); } - private static boolean isUnionTypeMatch(BUnionType sourceType, Type targetType, List unresolvedTypes) { - for (Type type : sourceType.getMemberTypes()) { - if (!checkIsType(type, targetType, unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean checkIsUnionType(Type sourceType, BUnionType targetType, List unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - sourceType = getImpliedType(sourceType); - TypePair pair = new TypePair(sourceType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - return switch (sourceType.getTag()) { - case TypeTags.UNION_TAG, - TypeTags.JSON_TAG, - TypeTags.ANYDATA_TAG -> isUnionTypeMatch((BUnionType) sourceType, targetType, unresolvedTypes); - case TypeTags.FINITE_TYPE_TAG -> isFiniteTypeMatch((BFiniteType) sourceType, targetType); - default -> { - for (Type type : targetType.getMemberTypes()) { - if (checkIsType(sourceType, type, unresolvedTypes)) { - yield true; - } - } - yield false; - } - }; + /** + * Check if two decimal values are equal in value. + * + * @param lhsValue The value on the left hand side + * @param rhsValue The value of the right hand side + * @return True if values are equal, else false. + */ + public static boolean checkDecimalEqual(DecimalValue lhsValue, DecimalValue rhsValue) { + return isDecimalRealNumber(lhsValue) && isDecimalRealNumber(rhsValue) && + lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) == 0; } - private static boolean checkIsMapType(Type sourceType, BMapType targetType, List unresolvedTypes) { - Type targetConstrainedType = targetType.getConstrainedType(); - sourceType = getImpliedType(sourceType); - switch (sourceType.getTag()) { - case TypeTags.MAP_TAG: - return checkConstraints(((BMapType) sourceType).getConstrainedType(), targetConstrainedType, - unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG: - BRecordType recType = (BRecordType) sourceType; - BUnionType wideTypeUnion = new BUnionType(getWideTypeComponents(recType)); - return checkConstraints(wideTypeUnion, targetConstrainedType, unresolvedTypes); - default: - return false; - } + public static boolean isNumericType(Type type) { + return Core.isSubType(context(), SemType.tryInto(type), NumericTypeHolder.NUMERIC_TYPE); } - private static boolean checkIsMapType(Object sourceVal, Type sourceType, BMapType targetType, - List unresolvedTypes) { - Type targetConstrainedType = targetType.getConstrainedType(); - sourceType = getImpliedType(sourceType); - return switch (sourceType.getTag()) { - case TypeTags.MAP_TAG -> checkConstraints(((BMapType) sourceType).getConstrainedType(), - targetConstrainedType, unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG -> checkIsMapType((MapValue) sourceVal, (BRecordType) sourceType, - unresolvedTypes, targetConstrainedType); - default -> false; - }; + public static boolean isByteLiteral(long longValue) { + return (longValue >= BBYTE_MIN_VALUE && longValue <= BBYTE_MAX_VALUE); } - private static boolean checkIsMapType(MapValue sourceVal, BRecordType sourceType, List unresolvedTypes, - Type targetConstrainedType) { - for (Field field : sourceType.getFields().values()) { - if (!SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY)) { - if (!checkIsType(field.getFieldType(), targetConstrainedType, unresolvedTypes)) { - return false; - } - continue; - } - - BString name = StringUtils.fromString(field.getFieldName()); - - if (SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL) && !sourceVal.containsKey(name)) { - continue; - } - - if (!checkIsLikeType(sourceVal.get(name), targetConstrainedType)) { - return false; - } - } - - if (sourceType.sealed) { - return true; - } + // Private methods - return checkIsType(sourceType.restFieldType, targetConstrainedType, unresolvedTypes); + private static boolean isSubTypeWithInherentType(Context cx, Object sourceValue, SemType target) { + return ShapeAnalyzer.inherentTypeOf(cx, sourceValue) + .map(source -> !Core.isEmpty(cx, source) && Core.isSubType(cx, source, target)) + // OR else do the normal type check by taking the shape of + .orElse(false); } - private static boolean checkIsXMLType(Type sourceType, Type targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - int sourceTag = sourceType.getTag(); - if (sourceTag == TypeTags.FINITE_TYPE_TAG) { - return isFiniteTypeMatch((BFiniteType) sourceType, targetType); - } - - BXmlType target = ((BXmlType) targetType); - if (sourceTag == TypeTags.XML_TAG) { - Type targetConstraint = getRecursiveTargetConstraintType(target); - BXmlType source = (BXmlType) sourceType; - if (source.constraint.getTag() == TypeTags.NEVER_TAG) { - if (targetConstraint.getTag() == TypeTags.UNION_TAG) { - return checkIsUnionType(sourceType, (BUnionType) targetConstraint, unresolvedTypes); - } - return targetConstraint.getTag() == TypeTags.XML_TEXT_TAG || - targetConstraint.getTag() == TypeTags.NEVER_TAG; - } - return checkIsType(source.constraint, targetConstraint, unresolvedTypes); - } - if (TypeTags.isXMLTypeTag(sourceTag)) { - return checkIsType(sourceType, target.constraint, unresolvedTypes); + private static synchronized boolean isSubType(Type source, Type target) { + if (source instanceof CacheableTypeDescriptor sourceCacheableType && + target instanceof CacheableTypeDescriptor targetCacheableType) { + return isSubTypeWithCache(sourceCacheableType, targetCacheableType); } - return false; + // This is really a workaround for Standard libraries that create record types that are not the "same". But + // with the same name and expect them to be same. + return isSubTypeInner(context(), source, target); } - private static Type getRecursiveTargetConstraintType(BXmlType target) { - Type targetConstraint = getImpliedType(target.constraint); - // TODO: Revisit and check why xml>> on chained iteration - while (targetConstraint.getTag() == TypeTags.XML_TAG) { - target = (BXmlType) targetConstraint; - targetConstraint = getImpliedType(target.constraint); - } - return targetConstraint; + private static boolean isSubTypeInner(Context cx, Type source, Type target) { + SemType sourceSemType = SemType.tryInto(source); + SemType targetSemType = SemType.tryInto(target); + return Core.isSubType(cx, sourceSemType, targetSemType); } - private static List getWideTypeComponents(BRecordType recType) { - List types = new ArrayList<>(); - for (Field f : recType.getFields().values()) { - types.add(f.getFieldType()); + private static boolean isSubTypeWithCache(CacheableTypeDescriptor source, CacheableTypeDescriptor target) { + Context cx = context(); + if (!source.shouldCache() || !target.shouldCache()) { + return isSubTypeInner(cx, source, target); } - if (!recType.sealed) { - types.add(recType.restFieldType); - } - return types; - } - - private static boolean checkIsStreamType(Type sourceType, BStreamType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.STREAM_TAG) { - return false; + Optional cachedResult = source.cachedTypeCheckResult(cx, target); + if (cachedResult.isPresent()) { + assert cachedResult.get() == isSubTypeInner(cx, source, target); + return cachedResult.get(); } - return checkConstraints(((BStreamType) sourceType).getConstrainedType(), targetType.getConstrainedType(), - unresolvedTypes) - && checkConstraints(((BStreamType) sourceType).getCompletionType(), targetType.getCompletionType(), - unresolvedTypes); + boolean result = isSubTypeInner(cx, source, target); + source.cacheTypeCheckResult(target, result); + return result; } - private static boolean checkIsTableType(Type sourceType, BTableType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.TABLE_TAG) { - return false; - } - - BTableType srcTableType = (BTableType) sourceType; - - if (!checkConstraints(srcTableType.getConstrainedType(), targetType.getConstrainedType(), - unresolvedTypes)) { - return false; - } - - if (targetType.getKeyType() == null && targetType.getFieldNames().length == 0) { - return true; + private static SemType widenedType(Context cx, Object value) { + if (value instanceof BValue bValue) { + return bValue.widenedType(); } - - if (targetType.getKeyType() != null) { - if (srcTableType.getKeyType() != null && - (checkConstraints(srcTableType.getKeyType(), targetType.getKeyType(), unresolvedTypes))) { - return true; - } - - if (srcTableType.getFieldNames().length == 0) { - return false; - } - - List fieldTypes = new ArrayList<>(); - Arrays.stream(srcTableType.getFieldNames()).forEach(field -> fieldTypes - .add(Objects.requireNonNull(getTableConstraintField(srcTableType.getConstrainedType(), field)) - .getFieldType())); - - if (fieldTypes.size() == 1) { - return checkConstraints(fieldTypes.get(0), targetType.getKeyType(), unresolvedTypes); - } - - BTupleType tupleType = new BTupleType(fieldTypes); - return checkConstraints(tupleType, targetType.getKeyType(), unresolvedTypes); + if (value == null) { + return Builder.getNilType(); + } else if (value instanceof Double) { + return Builder.getFloatType(); + } else if (value instanceof Number) { + return Builder.getIntType(); + } else if (value instanceof BString) { + return Builder.getStringType(); + } else if (value instanceof Boolean) { + return Builder.getBooleanType(); } - - return Arrays.equals(srcTableType.getFieldNames(), targetType.getFieldNames()); + throw new IllegalArgumentException("Unexpected object type"); } - static BField getTableConstraintField(Type constraintType, String fieldName) { - switch (constraintType.getTag()) { - case TypeTags.RECORD_TYPE_TAG: - Map fieldList = ((BRecordType) constraintType).getFields(); - return (BField) fieldList.get(fieldName); - case TypeTags.INTERSECTION_TAG: - Type effectiveType = ((BIntersectionType) constraintType).getEffectiveType(); - return getTableConstraintField(effectiveType, fieldName); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - Type referredType = ((BTypeReferenceType) constraintType).getReferredType(); - return getTableConstraintField(referredType, fieldName); - case TypeTags.UNION_TAG: - BUnionType unionType = (BUnionType) constraintType; - List memTypes = unionType.getMemberTypes(); - List fields = memTypes.stream().map(type -> getTableConstraintField(type, fieldName)) - .filter(Objects::nonNull).toList(); - - if (fields.size() != memTypes.size()) { - return null; - } - - if (fields.stream().allMatch(field -> isSameType(field.getFieldType(), fields.get(0).getFieldType()))) { - return fields.get(0); - } - return null; - default: - return null; - } + public static boolean isInherentlyImmutableType(Type sourceType) { + // readonly part is there to match to old API + return + Core.isSubType(context(), SemType.tryInto(sourceType), + InherentlyImmutableTypeHolder.INHERENTLY_IMMUTABLE_TYPE) || + sourceType instanceof ReadonlyType; } - private static boolean checkIsJSONType(Type sourceType, List unresolvedTypes) { - BJsonType jsonType = (BJsonType) TYPE_JSON; - sourceType = getImpliedType(sourceType); - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(sourceType, jsonType); - if (unresolvedTypes.contains(pair)) { + // NOTE: this is not the same as selectively immutable as it stated in the spec + public static boolean isSelectivelyImmutableType(Type type, Set unresolvedTypes) { + if (!unresolvedTypes.add(type)) { return true; } - unresolvedTypes.add(pair); - switch (sourceType.getTag()) { - case TypeTags.STRING_TAG: - case TypeTags.CHAR_STRING_TAG: - case TypeTags.INT_TAG: - case TypeTags.SIGNED32_INT_TAG: - case TypeTags.SIGNED16_INT_TAG: - case TypeTags.SIGNED8_INT_TAG: - case TypeTags.UNSIGNED32_INT_TAG: - case TypeTags.UNSIGNED16_INT_TAG: - case TypeTags.UNSIGNED8_INT_TAG: - case TypeTags.BYTE_TAG: - case TypeTags.FLOAT_TAG: - case TypeTags.DECIMAL_TAG: - case TypeTags.BOOLEAN_TAG: - case TypeTags.NULL_TAG: + switch (type.getTag()) { + case TypeTags.ANY_TAG: + case TypeTags.ANYDATA_TAG: case TypeTags.JSON_TAG: + case TypeTags.XML_TAG: + case TypeTags.XML_COMMENT_TAG: + case TypeTags.XML_ELEMENT_TAG: + case TypeTags.XML_PI_TAG: + case TypeTags.READONLY_TAG: return true; case TypeTags.ARRAY_TAG: - // Element type of the array should be 'is type' JSON - return checkIsType(((BArrayType) sourceType).getElementType(), jsonType, unresolvedTypes); - case TypeTags.FINITE_TYPE_TAG: - return isFiniteTypeMatch((BFiniteType) sourceType, jsonType); - case TypeTags.MAP_TAG: - return checkIsType(((BMapType) sourceType).getConstrainedType(), jsonType, unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG: - BRecordType recordType = (BRecordType) sourceType; - for (Field field : recordType.getFields().values()) { - if (!checkIsJSONType(field.getFieldType(), unresolvedTypes)) { + Type elementType = ((BArrayType) type).getElementType(); + return isInherentlyImmutableType(elementType) || + isSelectivelyImmutableType(elementType, unresolvedTypes); + case TypeTags.TUPLE_TAG: + BTupleType tupleType = (BTupleType) type; + for (Type tupMemType : tupleType.getTupleTypes()) { + if (!isInherentlyImmutableType(tupMemType) && + !isSelectivelyImmutableType(tupMemType, unresolvedTypes)) { return false; } } - if (!recordType.sealed) { - return checkIsJSONType(recordType.restFieldType, unresolvedTypes); + Type tupRestType = tupleType.getRestType(); + if (tupRestType == null) { + return true; } - return true; - case TypeTags.TUPLE_TAG: - BTupleType sourceTupleType = (BTupleType) sourceType; - for (Type memberType : sourceTupleType.getTupleTypes()) { - if (!checkIsJSONType(memberType, unresolvedTypes)) { - return false; - } - } - Type tupleRestType = sourceTupleType.getRestType(); - if (tupleRestType != null) { - return checkIsJSONType(tupleRestType, unresolvedTypes); - } - return true; - case TypeTags.UNION_TAG: - for (Type memberType : ((BUnionType) sourceType).getMemberTypes()) { - if (!checkIsJSONType(memberType, unresolvedTypes)) { - return false; - } - } - return true; - default: - return false; - } - } - - private static boolean checkIsRecordType(Type sourceType, BRecordType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - return switch (sourceType.getTag()) { - case TypeTags.RECORD_TYPE_TAG -> checkIsRecordType((BRecordType) sourceType, targetType, unresolvedTypes); - case TypeTags.MAP_TAG -> checkIsRecordType((BMapType) sourceType, targetType, unresolvedTypes); - default -> false; - }; - } - - private static boolean checkIsRecordType(BRecordType sourceRecordType, BRecordType targetType, - List unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(sourceRecordType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - // Unsealed records are not equivalent to sealed records, unless their rest field type is 'never'. But - // vice-versa is allowed. - if (targetType.sealed && !sourceRecordType.sealed && (sourceRecordType.restFieldType == null || - getImpliedType(sourceRecordType.restFieldType).getTag() != TypeTags.NEVER_TAG)) { - return false; - } - - // If both are sealed check the rest field type - if (!sourceRecordType.sealed && !targetType.sealed && - !checkIsType(sourceRecordType.restFieldType, targetType.restFieldType, unresolvedTypes)) { - return false; - } - - Map sourceFields = sourceRecordType.getFields(); - Set targetFieldNames = targetType.getFields().keySet(); - - for (Map.Entry targetFieldEntry : targetType.getFields().entrySet()) { - Field targetField = targetFieldEntry.getValue(); - Field sourceField = sourceFields.get(targetFieldEntry.getKey()); - - if (sourceField == null) { - if (!SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.OPTIONAL)) { - return false; - } - - if (!sourceRecordType.sealed && !checkIsType(sourceRecordType.restFieldType, targetField.getFieldType(), - unresolvedTypes)) { - return false; - } - - continue; - } - - if (hasIncompatibleReadOnlyFlags(targetField, sourceField)) { - return false; - } - - // If the target field is required, the source field should be required as well. - if (!SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.OPTIONAL) - && SymbolFlags.isFlagOn(sourceField.getFlags(), SymbolFlags.OPTIONAL)) { - return false; - } - - if (!checkIsType(sourceField.getFieldType(), targetField.getFieldType(), unresolvedTypes)) { - return false; - } - } - - // If there are fields remaining in the source record, first check if it's a closed record. Closed records - // should only have the fields specified by its type. - if (targetType.sealed) { - return targetFieldNames.containsAll(sourceFields.keySet()); - } - - // If it's an open record, check if they are compatible with the rest field of the target type. - for (Map.Entry sourceFieldEntry : sourceFields.entrySet()) { - if (targetFieldNames.contains(sourceFieldEntry.getKey())) { - continue; - } - - if (!checkIsType(sourceFieldEntry.getValue().getFieldType(), targetType.restFieldType, unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean checkIsRecordType(BMapType sourceType, BRecordType targetType, - List unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(sourceType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - if (targetType.sealed) { - return false; - } - - Type constraintType = sourceType.getConstrainedType(); - - for (Field field : targetType.getFields().values()) { - long flags = field.getFlags(); - if (!SymbolFlags.isFlagOn(flags, SymbolFlags.OPTIONAL)) { - return false; - } - - if (SymbolFlags.isFlagOn(flags, SymbolFlags.READONLY) && !sourceType.isReadOnly()) { - return false; - } - - if (!checkIsType(constraintType, field.getFieldType(), unresolvedTypes)) { - return false; - } - } - - return checkIsType(constraintType, targetType.restFieldType, unresolvedTypes); - } - - private static boolean checkRecordBelongsToAnydataType(MapValue sourceVal, BRecordType recordType, - List unresolvedTypes) { - Type targetType = TYPE_ANYDATA; - TypePair pair = new TypePair(recordType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - Map fields = recordType.getFields(); - - for (Map.Entry fieldEntry : fields.entrySet()) { - String fieldName = fieldEntry.getKey(); - Field field = fieldEntry.getValue(); - - if (SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY)) { - BString fieldNameBString = StringUtils.fromString(fieldName); - - if (SymbolFlags - .isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL) && !sourceVal.containsKey(fieldNameBString)) { - continue; - } - - if (!checkIsLikeType(sourceVal.get(fieldNameBString), targetType)) { - return false; - } - } else { - if (!checkIsType(field.getFieldType(), targetType, unresolvedTypes)) { - return false; - } - } - } - - if (recordType.sealed) { - return true; - } - - return checkIsType(recordType.restFieldType, targetType, unresolvedTypes); - } - - private static boolean checkIsRecordType(Object sourceVal, Type sourceType, BRecordType targetType, - List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - return switch (sourceType.getTag()) { - case TypeTags.RECORD_TYPE_TAG -> - checkIsRecordType((MapValue) sourceVal, (BRecordType) sourceType, targetType, unresolvedTypes); - case TypeTags.MAP_TAG -> checkIsRecordType((BMapType) sourceType, targetType, unresolvedTypes); - default -> false; - }; - } - - private static boolean checkIsRecordType(MapValue sourceRecordValue, BRecordType sourceRecordType, - BRecordType targetType, List unresolvedTypes) { - TypePair pair = new TypePair(sourceRecordType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - // Unsealed records are not equivalent to sealed records, unless their rest field type is 'never'. But - // vice-versa is allowed. - if (targetType.sealed && !sourceRecordType.sealed && (sourceRecordType.restFieldType == null || - getImpliedType(sourceRecordType.restFieldType).getTag() != TypeTags.NEVER_TAG)) { - return false; - } - - // If both are sealed check the rest field type - if (!sourceRecordType.sealed && !targetType.sealed && - !checkIsType(sourceRecordType.restFieldType, targetType.restFieldType, unresolvedTypes)) { - return false; - } - - Map sourceFields = sourceRecordType.getFields(); - Set targetFieldNames = targetType.getFields().keySet(); - - for (Map.Entry targetFieldEntry : targetType.getFields().entrySet()) { - String fieldName = targetFieldEntry.getKey(); - Field targetField = targetFieldEntry.getValue(); - Field sourceField = sourceFields.get(fieldName); - - if (getImpliedType(targetField.getFieldType()).getTag() == TypeTags.NEVER_TAG && - containsInvalidNeverField(sourceField, sourceRecordType)) { - return false; - } - - if (sourceField == null) { - if (!SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.OPTIONAL)) { - return false; - } - - if (!sourceRecordType.sealed && !checkIsType(sourceRecordType.restFieldType, targetField.getFieldType(), - unresolvedTypes)) { - return false; - } - - continue; - } - - if (hasIncompatibleReadOnlyFlags(targetField, sourceField)) { - return false; - } - - boolean optionalTargetField = SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.OPTIONAL); - boolean optionalSourceField = SymbolFlags.isFlagOn(sourceField.getFlags(), SymbolFlags.OPTIONAL); - - if (SymbolFlags.isFlagOn(sourceField.getFlags(), SymbolFlags.READONLY)) { - BString fieldNameBString = StringUtils.fromString(fieldName); - - if (optionalSourceField && !sourceRecordValue.containsKey(fieldNameBString)) { - if (!optionalTargetField) { - return false; - } - continue; - } - - if (!checkIsLikeType(sourceRecordValue.get(fieldNameBString), targetField.getFieldType())) { - return false; - } - } else { - if (!optionalTargetField && optionalSourceField) { - return false; - } - - if (!checkIsType(sourceField.getFieldType(), targetField.getFieldType(), unresolvedTypes)) { - return false; - } - } - } - - if (targetType.sealed) { - for (String sourceFieldName : sourceFields.keySet()) { - if (targetFieldNames.contains(sourceFieldName)) { - continue; - } - - if (!checkIsNeverTypeOrStructureTypeWithARequiredNeverMember( - sourceFields.get(sourceFieldName).getFieldType())) { - return false; - } - } - return true; - } - - for (Map.Entry targetFieldEntry : sourceFields.entrySet()) { - String fieldName = targetFieldEntry.getKey(); - Field field = targetFieldEntry.getValue(); - if (targetFieldNames.contains(fieldName)) { - continue; - } - - if (SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY)) { - if (!checkIsLikeType(sourceRecordValue.get(StringUtils.fromString(fieldName)), - targetType.restFieldType)) { - return false; - } - } else if (!checkIsType(field.getFieldType(), targetType.restFieldType, unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean containsInvalidNeverField(Field sourceField, BRecordType sourceRecordType) { - if (sourceField != null) { - return !containsNeverType(sourceField.getFieldType()); - } - if (sourceRecordType.isSealed()) { - return true; - } - return !containsNeverType(sourceRecordType.getRestFieldType()); - } - - private static boolean containsNeverType(Type fieldType) { - fieldType = getImpliedType(fieldType); - int fieldTag = fieldType.getTag(); - if (fieldTag == TypeTags.NEVER_TAG) { - return true; - } - if (fieldTag == TypeTags.UNION_TAG) { - List memberTypes = ((BUnionType) fieldType).getOriginalMemberTypes(); - for (Type member : memberTypes) { - if (getImpliedType(member).getTag() == TypeTags.NEVER_TAG) { - return true; - } - } - } - return false; - } - - private static boolean hasIncompatibleReadOnlyFlags(Field targetField, Field sourceField) { - return SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.READONLY) && !SymbolFlags - .isFlagOn(sourceField.getFlags(), - SymbolFlags.READONLY); - } - - private static boolean checkIsArrayType(BArrayType sourceType, BArrayType targetType, - List unresolvedTypes) { - switch (sourceType.getState()) { - case OPEN: - if (targetType.getState() != ArrayState.OPEN) { - return false; - } - break; - case CLOSED: - if (targetType.getState() == ArrayState.CLOSED && - sourceType.getSize() != targetType.getSize()) { - return false; - } - break; - default: - break; - } - return checkIsType(sourceType.getElementType(), targetType.getElementType(), unresolvedTypes); - } - - private static boolean checkIsArrayType(BTupleType sourceType, BArrayType targetType, - List unresolvedTypes) { - List tupleTypes = sourceType.getTupleTypes(); - Type sourceRestType = sourceType.getRestType(); - Type targetElementType = targetType.getElementType(); - - if (targetType.getState() == ArrayState.OPEN) { - for (Type sourceElementType : tupleTypes) { - if (!checkIsType(sourceElementType, targetElementType, unresolvedTypes)) { - return false; - } - } - if (sourceRestType != null) { - return checkIsType(sourceRestType, targetElementType, unresolvedTypes); - } - return true; - } - if (sourceRestType != null) { - return false; - } - if (tupleTypes.size() != targetType.getSize()) { - return false; - } - for (Type sourceElementType : tupleTypes) { - if (!checkIsType(sourceElementType, targetElementType, unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean checkIsArrayType(Type sourceType, BArrayType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - int sourceTypeTag = sourceType.getTag(); - - if (sourceTypeTag == TypeTags.UNION_TAG) { - for (Type memberType : ((BUnionType) sourceType).getMemberTypes()) { - if (!checkIsArrayType(memberType, targetType, unresolvedTypes)) { - return false; - } - } - return true; - } - - if (sourceTypeTag != TypeTags.ARRAY_TAG && sourceTypeTag != TypeTags.TUPLE_TAG) { - return false; - } - - if (sourceTypeTag == TypeTags.ARRAY_TAG) { - return checkIsArrayType((BArrayType) sourceType, targetType, unresolvedTypes); - } - return checkIsArrayType((BTupleType) sourceType, targetType, unresolvedTypes); - } - - private static boolean checkIsTupleType(BArrayType sourceType, BTupleType targetType, - List unresolvedTypes) { - Type sourceElementType = sourceType.getElementType(); - List targetTypes = targetType.getTupleTypes(); - Type targetRestType = targetType.getRestType(); - - switch (sourceType.getState()) { - case OPEN: - if (targetRestType == null) { - return false; - } - if (targetTypes.isEmpty()) { - return checkIsType(sourceElementType, targetRestType, unresolvedTypes); - } - return false; - case CLOSED: - if (sourceType.getSize() < targetTypes.size()) { - return false; - } - if (targetTypes.isEmpty()) { - if (targetRestType != null) { - return checkIsType(sourceElementType, targetRestType, unresolvedTypes); - } - return sourceType.getSize() == 0; - } - - for (Type targetElementType : targetTypes) { - if (!(checkIsType(sourceElementType, targetElementType, unresolvedTypes))) { - return false; - } - } - if (sourceType.getSize() == targetTypes.size()) { - return true; - } - if (targetRestType != null) { - return checkIsType(sourceElementType, targetRestType, unresolvedTypes); - } - return false; - default: - return false; - } - } - - private static boolean checkIsTupleType(BTupleType sourceType, BTupleType targetType, - List unresolvedTypes) { - List sourceTypes = sourceType.getTupleTypes(); - Type sourceRestType = sourceType.getRestType(); - List targetTypes = targetType.getTupleTypes(); - Type targetRestType = targetType.getRestType(); - - if (sourceRestType != null && targetRestType == null) { - return false; - } - int sourceTypeSize = sourceTypes.size(); - int targetTypeSize = targetTypes.size(); - - if (sourceRestType == null && targetRestType == null && sourceTypeSize != targetTypeSize) { - return false; - } - - if (sourceTypeSize < targetTypeSize) { - return false; - } - - for (int i = 0; i < targetTypeSize; i++) { - if (!checkIsType(sourceTypes.get(i), targetTypes.get(i), unresolvedTypes)) { - return false; - } - } - if (sourceTypeSize == targetTypeSize) { - if (sourceRestType != null) { - return checkIsType(sourceRestType, targetRestType, unresolvedTypes); - } - return true; - } - - for (int i = targetTypeSize; i < sourceTypeSize; i++) { - if (!checkIsType(sourceTypes.get(i), targetRestType, unresolvedTypes)) { - return false; - } - } - if (sourceRestType != null) { - return checkIsType(sourceRestType, targetRestType, unresolvedTypes); - } - return true; - } - - private static boolean checkIsTupleType(Type sourceType, BTupleType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - int sourceTypeTag = sourceType.getTag(); - - if (sourceTypeTag == TypeTags.UNION_TAG) { - for (Type memberType : ((BUnionType) sourceType).getMemberTypes()) { - if (!checkIsTupleType(memberType, targetType, unresolvedTypes)) { - return false; - } - } - return true; - } - - if (sourceTypeTag != TypeTags.ARRAY_TAG && sourceTypeTag != TypeTags.TUPLE_TAG) { - return false; - } - - if (sourceTypeTag == TypeTags.ARRAY_TAG) { - return checkIsTupleType((BArrayType) sourceType, targetType, unresolvedTypes); - } - return checkIsTupleType((BTupleType) sourceType, targetType, unresolvedTypes); - } - - private static boolean checkIsAnyType(Type sourceType) { - sourceType = getImpliedType(sourceType); - return switch (sourceType.getTag()) { - case TypeTags.ERROR_TAG, - TypeTags.READONLY_TAG -> false; - case TypeTags.UNION_TAG, - TypeTags.ANYDATA_TAG, - TypeTags.JSON_TAG -> { - for (Type memberType : ((BUnionType) sourceType).getMemberTypes()) { - if (!checkIsAnyType(memberType)) { - yield false; - } - } - yield true; - } - default -> true; - }; - } - - private static boolean checkIsFiniteType(Type sourceType, BFiniteType targetType) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.FINITE_TYPE_TAG) { - return false; - } - - BFiniteType sourceFiniteType = (BFiniteType) sourceType; - if (sourceFiniteType.valueSpace.size() != targetType.valueSpace.size()) { - return false; - } - - return targetType.valueSpace.containsAll(sourceFiniteType.valueSpace); - } - - private static boolean checkIsFutureType(Type sourceType, BFutureType targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.FUTURE_TAG) { - return false; - } - return checkConstraints(((BFutureType) sourceType).getConstrainedType(), targetType.getConstrainedType(), - unresolvedTypes); - } - - private static boolean checkObjectEquivalency(Type sourceType, BObjectType targetType, - List unresolvedTypes) { - return checkObjectEquivalency(null, sourceType, targetType, unresolvedTypes); - } - - private static boolean checkObjectEquivalency(Object sourceVal, Type sourceType, BObjectType targetType, - List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.OBJECT_TYPE_TAG && sourceType.getTag() != TypeTags.SERVICE_TAG) { - return false; - } - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(sourceType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - BObjectType sourceObjectType = (BObjectType) sourceType; - - if (SymbolFlags.isFlagOn(targetType.flags, SymbolFlags.ISOLATED) && - !SymbolFlags.isFlagOn(sourceObjectType.flags, SymbolFlags.ISOLATED)) { - return false; - } - - Map targetFields = targetType.getFields(); - Map sourceFields = sourceObjectType.getFields(); - List targetFuncs = getAllFunctionsList(targetType); - List sourceFuncs = getAllFunctionsList(sourceObjectType); - - if (targetType.getFields().values().stream().anyMatch(field -> SymbolFlags - .isFlagOn(field.getFlags(), SymbolFlags.PRIVATE)) - || targetFuncs.stream().anyMatch(func -> SymbolFlags.isFlagOn(func.getFlags(), - SymbolFlags.PRIVATE))) { - return false; - } - - if (targetFields.size() > sourceFields.size() || targetFuncs.size() > sourceFuncs.size()) { - return false; - } - - String targetTypeModule = Optional.ofNullable(targetType.getPackage()).map(Module::toString).orElse(""); - String sourceTypeModule = Optional.ofNullable(sourceObjectType.getPackage()).map(Module::toString).orElse(""); - - if (sourceVal == null) { - if (!checkObjectSubTypeForFields(targetFields, sourceFields, targetTypeModule, sourceTypeModule, - unresolvedTypes)) { - return false; - } - } else if (!checkObjectSubTypeForFieldsByValue(targetFields, sourceFields, targetTypeModule, sourceTypeModule, - (BObject) sourceVal, unresolvedTypes)) { - return false; - } - - return checkObjectSubTypeForMethods(unresolvedTypes, targetFuncs, sourceFuncs, targetTypeModule, - sourceTypeModule, sourceObjectType, targetType); - } - - private static List getAllFunctionsList(BObjectType objectType) { - List functionList = new ArrayList<>(Arrays.asList(objectType.getMethods())); - if (objectType.getTag() == TypeTags.SERVICE_TAG || - (objectType.flags & SymbolFlags.CLIENT) == SymbolFlags.CLIENT) { - Collections.addAll(functionList, ((BNetworkObjectType) objectType).getResourceMethods()); - } - - return functionList; - } - - private static boolean checkObjectSubTypeForFields(Map targetFields, - Map sourceFields, String targetTypeModule, - String sourceTypeModule, List unresolvedTypes) { - for (Field lhsField : targetFields.values()) { - Field rhsField = sourceFields.get(lhsField.getFieldName()); - if (rhsField == null || - !isInSameVisibilityRegion(targetTypeModule, sourceTypeModule, lhsField.getFlags(), - rhsField.getFlags()) || hasIncompatibleReadOnlyFlags(lhsField, - rhsField) || - !checkIsType(rhsField.getFieldType(), lhsField.getFieldType(), unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean checkObjectSubTypeForFieldsByValue(Map targetFields, - Map sourceFields, String targetTypeModule, - String sourceTypeModule, BObject sourceObjVal, - List unresolvedTypes) { - for (Field lhsField : targetFields.values()) { - String name = lhsField.getFieldName(); - Field rhsField = sourceFields.get(name); - if (rhsField == null || - !isInSameVisibilityRegion(targetTypeModule, sourceTypeModule, lhsField.getFlags(), - rhsField.getFlags()) || hasIncompatibleReadOnlyFlags(lhsField, - rhsField)) { - return false; - } - - if (SymbolFlags.isFlagOn(rhsField.getFlags(), SymbolFlags.FINAL)) { - Object fieldValue = sourceObjVal.get(StringUtils.fromString(name)); - Type fieldValueType = getType(fieldValue); - - if (fieldValueType.isReadOnly()) { - if (!checkIsLikeType(fieldValue, lhsField.getFieldType())) { - return false; - } - continue; - } - - if (!checkIsType(fieldValueType, lhsField.getFieldType(), unresolvedTypes)) { - return false; - } - } else if (!checkIsType(rhsField.getFieldType(), lhsField.getFieldType(), unresolvedTypes)) { - return false; - } - } - return true; - } - - private static boolean checkObjectSubTypeForMethods(List unresolvedTypes, - List targetFuncs, - List sourceFuncs, - String targetTypeModule, String sourceTypeModule, - BObjectType sourceType, BObjectType targetType) { - for (MethodType lhsFunc : targetFuncs) { - Optional rhsFunction = getMatchingInvokableType(sourceFuncs, lhsFunc, unresolvedTypes); - if (rhsFunction.isEmpty()) { - return false; - } - - MethodType rhsFunc = rhsFunction.get(); - if (!isInSameVisibilityRegion(targetTypeModule, sourceTypeModule, lhsFunc.getFlags(), rhsFunc.getFlags())) { - return false; - } - if (SymbolFlags.isFlagOn(lhsFunc.getFlags(), SymbolFlags.REMOTE) != SymbolFlags - .isFlagOn(rhsFunc.getFlags(), SymbolFlags.REMOTE)) { - return false; - } - } - - // Target type is not a distinct type, no need to match type-ids - BTypeIdSet targetTypeIdSet = targetType.typeIdSet; - if (targetTypeIdSet == null) { - return true; - } - - BTypeIdSet sourceTypeIdSet = sourceType.typeIdSet; - if (sourceTypeIdSet == null) { - return false; - } - - return sourceTypeIdSet.containsAll(targetTypeIdSet); - } - - private static boolean isInSameVisibilityRegion(String lhsTypePkg, String rhsTypePkg, long lhsFlags, - long rhsFlags) { - if (SymbolFlags.isFlagOn(lhsFlags, SymbolFlags.PRIVATE)) { - return lhsTypePkg.equals(rhsTypePkg); - } else if (SymbolFlags.isFlagOn(lhsFlags, SymbolFlags.PUBLIC)) { - return SymbolFlags.isFlagOn(rhsFlags, SymbolFlags.PUBLIC); - } - return !SymbolFlags.isFlagOn(rhsFlags, SymbolFlags.PRIVATE) && !SymbolFlags - .isFlagOn(rhsFlags, SymbolFlags.PUBLIC) && - lhsTypePkg.equals(rhsTypePkg); - } - - private static Optional getMatchingInvokableType(List rhsFuncs, - MethodType lhsFunc, - List unresolvedTypes) { - Optional matchingFunction = rhsFuncs.stream() - .filter(rhsFunc -> lhsFunc.getName().equals(rhsFunc.getName())) - .filter(rhsFunc -> checkFunctionTypeEqualityForObjectType(rhsFunc.getType(), lhsFunc.getType(), - unresolvedTypes)) - .findFirst(); - - if (matchingFunction.isEmpty()) { - return matchingFunction; - } - // For resource function match, we need to check whether lhs function resource path type belongs to - // rhs function resource path type - MethodType matchingFunc = matchingFunction.get(); - boolean lhsFuncIsResource = SymbolFlags.isFlagOn(lhsFunc.getFlags(), SymbolFlags.RESOURCE); - boolean matchingFuncIsResource = SymbolFlags.isFlagOn(matchingFunc.getFlags(), SymbolFlags.RESOURCE); - - if (!lhsFuncIsResource && !matchingFuncIsResource) { - return matchingFunction; - } - - if (!lhsFuncIsResource || !matchingFuncIsResource) { - return Optional.empty(); - } - - Type[] lhsFuncResourcePathTypes = ((BResourceMethodType) lhsFunc).pathSegmentTypes; - Type[] rhsFuncResourcePathTypes = ((BResourceMethodType) matchingFunc).pathSegmentTypes; - - int lhsFuncResourcePathTypesSize = lhsFuncResourcePathTypes.length; - if (lhsFuncResourcePathTypesSize != rhsFuncResourcePathTypes.length) { - return Optional.empty(); - } - - for (int i = 0; i < lhsFuncResourcePathTypesSize; i++) { - if (!checkIsType(lhsFuncResourcePathTypes[i], rhsFuncResourcePathTypes[i])) { - return Optional.empty(); - } - } - - return matchingFunction; - } - - private static boolean checkFunctionTypeEqualityForObjectType(FunctionType source, FunctionType target, - List unresolvedTypes) { - if (hasIncompatibleIsolatedFlags(target, source)) { - return false; - } - - if (source.getParameters().length != target.getParameters().length) { - return false; - } - - for (int i = 0; i < source.getParameters().length; i++) { - if (!checkIsType(target.getParameters()[i].type, source.getParameters()[i].type, unresolvedTypes)) { - return false; - } - } - - if (source.getReturnType() == null && target.getReturnType() == null) { - return true; - } else if (source.getReturnType() == null || target.getReturnType() == null) { - return false; - } - - return checkIsType(source.getReturnType(), target.getReturnType(), unresolvedTypes); - } - - private static boolean checkIsFunctionType(Type sourceType, BFunctionType targetType) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() != TypeTags.FUNCTION_POINTER_TAG) { - return false; - } - - BFunctionType source = (BFunctionType) sourceType; - if (hasIncompatibleIsolatedFlags(targetType, source) || hasIncompatibleTransactionalFlags(targetType, source)) { - return false; - } - - if (SymbolFlags.isFlagOn(targetType.getFlags(), SymbolFlags.ANY_FUNCTION)) { - return true; - } - - if (source.parameters.length != targetType.parameters.length) { - return false; - } - - for (int i = 0; i < source.parameters.length; i++) { - if (!checkIsType(targetType.parameters[i].type, source.parameters[i].type, new ArrayList<>())) { - return false; - } - } - - return checkIsType(source.retType, targetType.retType, new ArrayList<>()); - } - - private static boolean hasIncompatibleIsolatedFlags(FunctionType target, FunctionType source) { - return SymbolFlags.isFlagOn(target.getFlags(), SymbolFlags.ISOLATED) && !SymbolFlags - .isFlagOn(source.getFlags(), SymbolFlags.ISOLATED); - } - - private static boolean hasIncompatibleTransactionalFlags(FunctionType target, FunctionType source) { - return SymbolFlags.isFlagOn(source.getFlags(), SymbolFlags.TRANSACTIONAL) && !SymbolFlags - .isFlagOn(target.getFlags(), SymbolFlags.TRANSACTIONAL); - } - - private static boolean checkIsServiceType(Type sourceType, Type targetType, List unresolvedTypes) { - sourceType = getImpliedType(sourceType); - if (sourceType.getTag() == TypeTags.SERVICE_TAG) { - return checkObjectEquivalency(sourceType, (BObjectType) targetType, unresolvedTypes); - } - - if (sourceType.getTag() == TypeTags.OBJECT_TYPE_TAG) { - long flags = ((BObjectType) sourceType).flags; - return (flags & SymbolFlags.SERVICE) == SymbolFlags.SERVICE; - } - - return false; - } - - public static boolean isInherentlyImmutableType(Type sourceType) { - sourceType = getImpliedType(sourceType); - if (isSimpleBasicType(sourceType)) { - return true; - } - - return switch (sourceType.getTag()) { - case TypeTags.XML_TEXT_TAG, - TypeTags.FINITE_TYPE_TAG, // Assuming a finite type will only have members from simple basic types. - TypeTags.READONLY_TAG, - TypeTags.NULL_TAG, - TypeTags.NEVER_TAG, - TypeTags.ERROR_TAG, - TypeTags.INVOKABLE_TAG, - TypeTags.SERVICE_TAG, - TypeTags.TYPEDESC_TAG, - TypeTags.FUNCTION_POINTER_TAG, - TypeTags.HANDLE_TAG, - TypeTags.REG_EXP_TYPE_TAG -> true; - case TypeTags.XML_TAG -> ((BXmlType) sourceType).constraint.getTag() == TypeTags.NEVER_TAG; - case TypeTags.TYPE_REFERENCED_TYPE_TAG -> - isInherentlyImmutableType(((BTypeReferenceType) sourceType).getReferredType()); - default -> false; - }; - } - - public static boolean isSelectivelyImmutableType(Type type, Set unresolvedTypes) { - if (!unresolvedTypes.add(type)) { - return true; - } - - switch (type.getTag()) { - case TypeTags.ANY_TAG: - case TypeTags.ANYDATA_TAG: - case TypeTags.JSON_TAG: - case TypeTags.XML_TAG: - case TypeTags.XML_COMMENT_TAG: - case TypeTags.XML_ELEMENT_TAG: - case TypeTags.XML_PI_TAG: - return true; - case TypeTags.ARRAY_TAG: - Type elementType = ((BArrayType) type).getElementType(); - return isInherentlyImmutableType(elementType) || - isSelectivelyImmutableType(elementType, unresolvedTypes); - case TypeTags.TUPLE_TAG: - BTupleType tupleType = (BTupleType) type; - for (Type tupMemType : tupleType.getTupleTypes()) { - if (!isInherentlyImmutableType(tupMemType) && - !isSelectivelyImmutableType(tupMemType, unresolvedTypes)) { - return false; - } - } - - Type tupRestType = tupleType.getRestType(); - if (tupRestType == null) { - return true; - } - - return isInherentlyImmutableType(tupRestType) || - isSelectivelyImmutableType(tupRestType, unresolvedTypes); - case TypeTags.RECORD_TYPE_TAG: - BRecordType recordType = (BRecordType) type; - for (Field field : recordType.getFields().values()) { - Type fieldType = field.getFieldType(); - if (!isInherentlyImmutableType(fieldType) && - !isSelectivelyImmutableType(fieldType, unresolvedTypes)) { - return false; - } - } - - Type recordRestType = recordType.restFieldType; - if (recordRestType == null) { - return true; - } - - return isInherentlyImmutableType(recordRestType) || - isSelectivelyImmutableType(recordRestType, unresolvedTypes); - case TypeTags.OBJECT_TYPE_TAG: - BObjectType objectType = (BObjectType) type; - - if (SymbolFlags.isFlagOn(objectType.flags, SymbolFlags.CLASS) && - !SymbolFlags.isFlagOn(objectType.flags, SymbolFlags.READONLY)) { - return false; - } - - for (Field field : objectType.getFields().values()) { - Type fieldType = field.getFieldType(); - if (!isInherentlyImmutableType(fieldType) && - !isSelectivelyImmutableType(fieldType, unresolvedTypes)) { - return false; - } - } - return true; - case TypeTags.MAP_TAG: - Type constraintType = ((BMapType) type).getConstrainedType(); - return isInherentlyImmutableType(constraintType) || - isSelectivelyImmutableType(constraintType, unresolvedTypes); - case TypeTags.TABLE_TAG: - Type tableConstraintType = ((BTableType) type).getConstrainedType(); - return isInherentlyImmutableType(tableConstraintType) || - isSelectivelyImmutableType(tableConstraintType, unresolvedTypes); - case TypeTags.UNION_TAG: - boolean readonlyIntersectionExists = false; - for (Type memberType : ((BUnionType) type).getMemberTypes()) { - if (isInherentlyImmutableType(memberType) || - isSelectivelyImmutableType(memberType, unresolvedTypes)) { - readonlyIntersectionExists = true; - break; - } - } - return readonlyIntersectionExists; - case TypeTags.INTERSECTION_TAG: - return isSelectivelyImmutableType(((BIntersectionType) type).getEffectiveType(), unresolvedTypes); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - return isSelectivelyImmutableType(((BTypeReferenceType) type).getReferredType(), unresolvedTypes); - default: - return false; - } - } - - private static boolean checkConstraints(Type sourceConstraint, Type targetConstraint, - List unresolvedTypes) { - if (sourceConstraint == null) { - sourceConstraint = TYPE_ANY; - } - - if (targetConstraint == null) { - targetConstraint = TYPE_ANY; - } - - return checkIsType(sourceConstraint, targetConstraint, unresolvedTypes); - } - - private static boolean isMutable(Object value, Type sourceType) { - // All the value types are immutable - sourceType = getImpliedType(sourceType); - if (value == null || sourceType.getTag() < TypeTags.NULL_TAG || - sourceType.getTag() == TypeTags.FINITE_TYPE_TAG) { - return false; - } - - return !((BRefValue) value).isFrozen(); - } - - private static boolean checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(Type type) { - Set visitedTypeSet = new HashSet<>(); - return checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(type, visitedTypeSet); - } - - private static boolean checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(Type type, - Set visitedTypeSet) { - switch (type.getTag()) { - case TypeTags.NEVER_TAG: - return true; - case TypeTags.RECORD_TYPE_TAG: - BRecordType recordType = (BRecordType) type; - visitedTypeSet.add(recordType.getName()); - for (Field field : recordType.getFields().values()) { - // skip check for fields with self referencing type and not required fields. - if ((SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.REQUIRED) || - !SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL)) && - !visitedTypeSet.contains(field.getFieldType()) && - checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(field.getFieldType(), - visitedTypeSet)) { - return true; - } - } - return false; - case TypeTags.TUPLE_TAG: - BTupleType tupleType = (BTupleType) type; - visitedTypeSet.add(tupleType.getName()); - List tupleTypes = tupleType.getTupleTypes(); - for (Type mem : tupleTypes) { - if (!visitedTypeSet.add(mem.getName())) { - continue; - } - if (checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(mem, visitedTypeSet)) { - return true; - } - } - return false; - case TypeTags.ARRAY_TAG: - BArrayType arrayType = (BArrayType) type; - visitedTypeSet.add(arrayType.getName()); - Type elemType = arrayType.getElementType(); - visitedTypeSet.add(elemType.getName()); - return arrayType.getState() != ArrayState.OPEN && - checkIsNeverTypeOrStructureTypeWithARequiredNeverMember(elemType, visitedTypeSet); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - return checkIsNeverTypeOrStructureTypeWithARequiredNeverMember( - ((BTypeReferenceType) type).getReferredType(), visitedTypeSet); - case TypeTags.INTERSECTION_TAG: - return checkIsNeverTypeOrStructureTypeWithARequiredNeverMember( - ((BIntersectionType) type).getEffectiveType(), visitedTypeSet); - default: - return false; - } - } - - /** - * Check whether a given value confirms to a given type. First it checks if the type of the value, and - * if fails then falls back to checking the value. - * - * @param errors list to collect typecast errors - * @param sourceValue Value to check - * @param targetType Target type - * @param unresolvedValues Values that are unresolved so far - * @param allowNumericConversion Flag indicating whether to perform numeric conversions - * @param varName variable name to identify the parent of a record field - * @return True if the value confirms to the provided type. False, otherwise. - */ - private static boolean checkIsLikeType(List errors, Object sourceValue, Type targetType, - List unresolvedValues, - boolean allowNumericConversion, String varName) { - Type sourceType = getType(sourceValue); - if (checkIsType(sourceType, targetType, new ArrayList<>())) { - return true; - } - - return checkIsLikeOnValue(errors, sourceValue, sourceType, targetType, unresolvedValues, allowNumericConversion, - varName); - } - - /** - * Check whether a given value confirms to a given type. Strictly checks the value only, and does not consider the - * type of the value for consideration. - * - * @param errors list to collect typecast errors - * @param sourceValue Value to check - * @param sourceType Type of the value - * @param targetType Target type - * @param unresolvedValues Values that are unresolved so far - * @param allowNumericConversion Flag indicating whether to perform numeric conversions - * @param varName variable name to identify the parent of a record field - * @return True if the value confirms to the provided type. False, otherwise. - */ - private static boolean checkIsLikeOnValue(List errors, Object sourceValue, Type sourceType, Type targetType, - List unresolvedValues, boolean allowNumericConversion, - String varName) { - int sourceTypeTag = sourceType.getTag(); - int targetTypeTag = targetType.getTag(); - - switch (sourceTypeTag) { - case TypeTags.INTERSECTION_TAG: - return checkIsLikeOnValue(errors, sourceValue, ((BIntersectionType) sourceType).getEffectiveType(), - targetTypeTag != TypeTags.INTERSECTION_TAG ? targetType : - ((BIntersectionType) targetType).getEffectiveType(), - unresolvedValues, allowNumericConversion, varName); - case TypeTags.PARAMETERIZED_TYPE_TAG: - if (targetTypeTag != TypeTags.PARAMETERIZED_TYPE_TAG) { - return checkIsLikeOnValue(errors, sourceValue, - ((BParameterizedType) sourceType).getParamValueType(), targetType, unresolvedValues, - allowNumericConversion, varName); - } - return checkIsLikeOnValue(errors, sourceValue, ((BParameterizedType) sourceType).getParamValueType(), - ((BParameterizedType) targetType).getParamValueType(), unresolvedValues, - allowNumericConversion, varName); - default: - break; - } - - return switch (targetTypeTag) { - case TypeTags.READONLY_TAG -> true; - case TypeTags.BYTE_TAG -> { - if (TypeTags.isIntegerTypeTag(sourceTypeTag)) { - yield isByteLiteral((Long) sourceValue); - } - yield allowNumericConversion && TypeConverter.isConvertibleToByte(sourceValue); - } - case TypeTags.INT_TAG -> allowNumericConversion && TypeConverter.isConvertibleToInt(sourceValue); - case TypeTags.SIGNED32_INT_TAG, - TypeTags.SIGNED16_INT_TAG, - TypeTags.SIGNED8_INT_TAG, - TypeTags.UNSIGNED32_INT_TAG, - TypeTags.UNSIGNED16_INT_TAG, - TypeTags.UNSIGNED8_INT_TAG -> { - if (TypeTags.isIntegerTypeTag(sourceTypeTag)) { - yield TypeConverter.isConvertibleToIntSubType(sourceValue, targetType); - } - yield allowNumericConversion && TypeConverter.isConvertibleToIntSubType(sourceValue, targetType); - } - case TypeTags.FLOAT_TAG, - TypeTags.DECIMAL_TAG -> - allowNumericConversion && TypeConverter.isConvertibleToFloatingPointTypes(sourceValue); - case TypeTags.CHAR_STRING_TAG -> TypeConverter.isConvertibleToChar(sourceValue); - case TypeTags.RECORD_TYPE_TAG -> - checkIsLikeRecordType(sourceValue, (BRecordType) targetType, unresolvedValues, - allowNumericConversion, varName, errors); - case TypeTags.TABLE_TAG -> checkIsLikeTableType(sourceValue, (BTableType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.JSON_TAG -> - checkIsLikeJSONType(sourceValue, sourceType, (BJsonType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.MAP_TAG -> - checkIsLikeMapType(sourceValue, (BMapType) targetType, unresolvedValues, allowNumericConversion); - case TypeTags.STREAM_TAG -> checkIsLikeStreamType(sourceValue, (BStreamType) targetType); - case TypeTags.ARRAY_TAG -> checkIsLikeArrayType(sourceValue, (BArrayType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.TUPLE_TAG -> checkIsLikeTupleType(sourceValue, (BTupleType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.ERROR_TAG -> checkIsLikeErrorType(sourceValue, (BErrorType) targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.ANYDATA_TAG -> - checkIsLikeAnydataType(sourceValue, sourceType, unresolvedValues, allowNumericConversion); - case TypeTags.FINITE_TYPE_TAG -> - checkFiniteTypeAssignable(sourceValue, sourceType, (BFiniteType) targetType, - unresolvedValues, allowNumericConversion); - case TypeTags.XML_ELEMENT_TAG, - TypeTags.XML_COMMENT_TAG, - TypeTags.XML_PI_TAG, - TypeTags.XML_TEXT_TAG -> { - if (TypeTags.isXMLTypeTag(sourceTypeTag)) { - yield checkIsLikeXmlValueSingleton((XmlValue) sourceValue, targetType); - } - yield false; - } - case TypeTags.XML_TAG -> { - if (TypeTags.isXMLTypeTag(sourceTypeTag)) { - yield checkIsLikeXMLSequenceType((XmlValue) sourceValue, targetType); - } - yield false; - } - case TypeTags.UNION_TAG -> - checkIsLikeUnionType(errors, sourceValue, (BUnionType) targetType, unresolvedValues, - allowNumericConversion, varName); - case TypeTags.INTERSECTION_TAG -> checkIsLikeOnValue(errors, sourceValue, sourceType, - ((BIntersectionType) targetType).getEffectiveType(), unresolvedValues, allowNumericConversion, - varName); - case TypeTags.TYPE_REFERENCED_TYPE_TAG -> checkIsLikeOnValue(errors, sourceValue, sourceType, - ((BTypeReferenceType) targetType).getReferredType(), unresolvedValues, allowNumericConversion, - varName); - default -> false; - }; - } - - private static boolean checkIsLikeUnionType(List errors, Object sourceValue, BUnionType targetType, - List unresolvedValues, boolean allowNumericConversion, - String varName) { - if (allowNumericConversion) { - List compatibleTypesWithNumConversion = new ArrayList<>(); - List compatibleTypesWithoutNumConversion = new ArrayList<>(); - for (Type type : targetType.getMemberTypes()) { - List tempList = new ArrayList<>(unresolvedValues.size()); - tempList.addAll(unresolvedValues); - - if (checkIsLikeType(null, sourceValue, type, tempList, false, varName)) { - compatibleTypesWithoutNumConversion.add(type); - } - - if (checkIsLikeType(null, sourceValue, type, unresolvedValues, true, varName)) { - compatibleTypesWithNumConversion.add(type); - } - } - // Conversion should only be possible to one other numeric type. - return !compatibleTypesWithNumConversion.isEmpty() && - compatibleTypesWithNumConversion.size() - compatibleTypesWithoutNumConversion.size() <= 1; - } else { - return checkIsLikeUnionType(errors, sourceValue, targetType, unresolvedValues, varName); - } - } - - private static boolean checkIsLikeUnionType(List errors, Object sourceValue, BUnionType targetType, - List unresolvedValues, String varName) { - if (errors == null) { - for (Type type : targetType.getMemberTypes()) { - if (checkIsLikeType(null, sourceValue, type, unresolvedValues, false, varName)) { - return true; - } - } - } else { - int initialErrorCount; - errors.add(ERROR_MESSAGE_UNION_START); - int initialErrorListSize = errors.size(); - for (Type type : targetType.getMemberTypes()) { - initialErrorCount = errors.size(); - if (checkIsLikeType(errors, sourceValue, type, unresolvedValues, false, varName)) { - errors.subList(initialErrorListSize - 1, errors.size()).clear(); - return true; - } - if (initialErrorCount != errors.size()) { - errors.add(ERROR_MESSAGE_UNION_SEPARATOR); - } - } - int currentErrorListSize = errors.size(); - errors.remove(currentErrorListSize - 1); - if (initialErrorListSize != currentErrorListSize) { - errors.add(ERROR_MESSAGE_UNION_END); - } - } - return false; - } - private static XmlNodeType getXmlNodeType(Type type) { - return switch (getImpliedType(type).getTag()) { - case TypeTags.XML_ELEMENT_TAG -> XmlNodeType.ELEMENT; - case TypeTags.XML_COMMENT_TAG -> XmlNodeType.COMMENT; - case TypeTags.XML_PI_TAG -> XmlNodeType.PI; - default -> XmlNodeType.TEXT; - }; - } - - private static boolean checkIsLikeXmlValueSingleton(XmlValue xmlSource, Type targetType) { - XmlNodeType targetXmlNodeType = getXmlNodeType(targetType); - XmlNodeType xmlSourceNodeType = xmlSource.getNodeType(); - - if (xmlSourceNodeType == targetXmlNodeType) { - return true; - } - - if (xmlSourceNodeType == XmlNodeType.SEQUENCE) { - XmlSequence seq = (XmlSequence) xmlSource; - return seq.size() == 1 && seq.getChildrenList().get(0).getNodeType() == targetXmlNodeType || - (targetXmlNodeType == XmlNodeType.TEXT && seq.isEmpty()); - } - - return false; - } - - private static void populateTargetXmlNodeTypes(Set nodeTypes, Type targetType) { - // there are only 4 xml subtypes - if (nodeTypes.size() == 4) { - return; - } - - Type referredType = getImpliedType(targetType); - switch (referredType.getTag()) { - case TypeTags.UNION_TAG: - for (Type memberType : ((UnionType) referredType).getMemberTypes()) { - populateTargetXmlNodeTypes(nodeTypes, memberType); - } - break; - case TypeTags.INTERSECTION_TAG: - populateTargetXmlNodeTypes(nodeTypes, ((IntersectionType) referredType).getEffectiveType()); - break; - case TypeTags.XML_ELEMENT_TAG: - nodeTypes.add(XmlNodeType.ELEMENT); - break; - case TypeTags.XML_COMMENT_TAG: - nodeTypes.add(XmlNodeType.COMMENT); - break; - case TypeTags.XML_PI_TAG: - nodeTypes.add(XmlNodeType.PI); - break; - case TypeTags.XML_TEXT_TAG: - nodeTypes.add(XmlNodeType.TEXT); - break; - case TypeTags.XML_TAG: - populateTargetXmlNodeTypes(nodeTypes, ((BXmlType) referredType).constraint); - break; - default: - break; - - } - } - - private static boolean checkIsLikeXMLSequenceType(XmlValue xmlSource, Type targetType) { - Set acceptedNodeTypes = new HashSet<>(); - populateTargetXmlNodeTypes(acceptedNodeTypes, targetType); - - XmlNodeType xmlSourceNodeType = xmlSource.getNodeType(); - if (xmlSourceNodeType != XmlNodeType.SEQUENCE) { - return acceptedNodeTypes.contains(xmlSourceNodeType); - } - - XmlSequence seq = (XmlSequence) xmlSource; - for (BXml m : seq.getChildrenList()) { - if (!acceptedNodeTypes.contains(m.getNodeType())) { - return false; - } - } - return true; - } - - public static boolean isNumericType(Type type) { - type = getImpliedType(type); - return type.getTag() < TypeTags.STRING_TAG || TypeTags.isIntegerTypeTag(type.getTag()); - } - - private static boolean checkIsLikeAnydataType(Object sourceValue, Type sourceType, - List unresolvedValues, - boolean allowNumericConversion) { - sourceType = getImpliedType(sourceType); - switch (sourceType.getTag()) { + return isInherentlyImmutableType(tupRestType) || + isSelectivelyImmutableType(tupRestType, unresolvedTypes); case TypeTags.RECORD_TYPE_TAG: - case TypeTags.MAP_TAG: - return isLikeAnydataType(((MapValueImpl) sourceValue).values().toArray(), - unresolvedValues, allowNumericConversion); - case TypeTags.TABLE_TAG: - return isLikeAnydataType(((TableValueImpl) sourceValue).values().toArray(), - unresolvedValues, allowNumericConversion); - case TypeTags.ARRAY_TAG: - ArrayValue arr = (ArrayValue) sourceValue; - BArrayType arrayType = (BArrayType) getImpliedType(arr.getType()); - return switch (getImpliedType(arrayType.getElementType()).getTag()) { - case TypeTags.INT_TAG, - TypeTags.FLOAT_TAG, - TypeTags.DECIMAL_TAG, - TypeTags.STRING_TAG, - TypeTags.BOOLEAN_TAG, - TypeTags.BYTE_TAG -> true; - default -> isLikeAnydataType(arr.getValues(), unresolvedValues, allowNumericConversion); - }; - case TypeTags.TUPLE_TAG: - return isLikeAnydataType(((ArrayValue) sourceValue).getValues(), unresolvedValues, - allowNumericConversion); - default: - return sourceType.isAnydata(); - } - } - - private static boolean isLikeAnydataType(Object[] objects, List unresolvedValues, - boolean allowNumericConversion) { - for (Object value : objects) { - if (!checkIsLikeType(null, value, TYPE_ANYDATA, unresolvedValues, allowNumericConversion, - null)) { - return false; - } - } - return true; - } - - private static boolean checkIsLikeTupleType(Object sourceValue, BTupleType targetType, - List unresolvedValues, boolean allowNumericConversion) { - if (!(sourceValue instanceof ArrayValue source)) { - return false; - } - - List targetTypes = targetType.getTupleTypes(); - int sourceTypeSize = source.size(); - int targetTypeSize = targetTypes.size(); - Type targetRestType = targetType.getRestType(); - - if (sourceTypeSize < targetTypeSize) { - return false; - } - if (targetRestType == null && sourceTypeSize > targetTypeSize) { - return false; - } - - for (int i = 0; i < targetTypeSize; i++) { - if (!checkIsLikeType(null, source.getRefValue(i), targetTypes.get(i), unresolvedValues, - allowNumericConversion, null)) { - return false; - } - } - for (int i = targetTypeSize; i < sourceTypeSize; i++) { - if (!checkIsLikeType(null, source.getRefValue(i), targetRestType, unresolvedValues, - allowNumericConversion, null)) { - return false; - } - } - return true; - } - - public static boolean isByteLiteral(long longValue) { - return (longValue >= BBYTE_MIN_VALUE && longValue <= BBYTE_MAX_VALUE); - } - - static boolean isSigned32LiteralValue(Long longObject) { - - return (longObject >= SIGNED32_MIN_VALUE && longObject <= SIGNED32_MAX_VALUE); - } - - static boolean isSigned16LiteralValue(Long longObject) { - - return (longObject.intValue() >= SIGNED16_MIN_VALUE && longObject.intValue() <= SIGNED16_MAX_VALUE); - } - - static boolean isSigned8LiteralValue(Long longObject) { - - return (longObject.intValue() >= SIGNED8_MIN_VALUE && longObject.intValue() <= SIGNED8_MAX_VALUE); - } - - static boolean isUnsigned32LiteralValue(Long longObject) { - - return (longObject >= 0 && longObject <= UNSIGNED32_MAX_VALUE); - } - - static boolean isUnsigned16LiteralValue(Long longObject) { - - return (longObject.intValue() >= 0 && longObject.intValue() <= UNSIGNED16_MAX_VALUE); - } - - static boolean isUnsigned8LiteralValue(Long longObject) { - - return (longObject.intValue() >= 0 && longObject.intValue() <= UNSIGNED8_MAX_VALUE); - } - - static boolean isCharLiteralValue(Object object) { - String value; - if (object instanceof BString bString) { - value = bString.getValue(); - } else if (object instanceof String s) { - value = s; - } else { - return false; - } - return value.codePoints().count() == 1; - } - - private static boolean checkIsLikeArrayType(Object sourceValue, BArrayType targetType, - List unresolvedValues, boolean allowNumericConversion) { - if (!(sourceValue instanceof ArrayValue source)) { - return false; - } - - Type targetTypeElementType = targetType.getElementType(); - if (source.getType().getTag() == TypeTags.ARRAY_TAG) { - Type sourceElementType = ((BArrayType) source.getType()).getElementType(); - if (isValueType(sourceElementType)) { - - if (checkIsType(sourceElementType, targetTypeElementType, new ArrayList<>())) { - return true; - } - - if (allowNumericConversion && isNumericType(sourceElementType)) { - if (isNumericType(targetTypeElementType)) { - return true; - } - - if (targetTypeElementType.getTag() != TypeTags.UNION_TAG) { + BRecordType recordType = (BRecordType) type; + for (Field field : recordType.getFields().values()) { + Type fieldType = field.getFieldType(); + if (!isInherentlyImmutableType(fieldType) && + !isSelectivelyImmutableType(fieldType, unresolvedTypes)) { return false; } - - List targetNumericTypes = new ArrayList<>(); - for (Type memType : ((BUnionType) targetTypeElementType).getMemberTypes()) { - if (isNumericType(memType) && !targetNumericTypes.contains(memType)) { - targetNumericTypes.add(memType); - } - } - return targetNumericTypes.size() == 1; } - if (targetTypeElementType.getTag() == TypeTags.FLOAT_TAG || - targetTypeElementType.getTag() == TypeTags.DECIMAL_TAG) { - return false; + Type recordRestType = recordType.restFieldType; + if (recordRestType == null) { + return true; } - } - } - - int sourceSize = source.size(); - if ((targetType.getState() != ArrayState.OPEN) && (sourceSize != targetType.getSize())) { - return false; - } - for (int i = 0; i < sourceSize; i++) { - if (!checkIsLikeType(null, source.get(i), targetTypeElementType, unresolvedValues, - allowNumericConversion, null)) { - return false; - } - } - return true; - } - private static boolean checkIsLikeMapType(Object sourceValue, BMapType targetType, - List unresolvedValues, boolean allowNumericConversion) { - if (!(sourceValue instanceof MapValueImpl sourceMapValue)) { - return false; - } - - for (Object mapEntry : sourceMapValue.values()) { - if (!checkIsLikeType(null, mapEntry, targetType.getConstrainedType(), unresolvedValues, - allowNumericConversion, null)) { - return false; - } - } - return true; - } - - private static boolean checkIsLikeStreamType(Object sourceValue, BStreamType targetType) { - if (!(sourceValue instanceof StreamValue streamValue)) { - return false; - } - - BStreamType streamType = (BStreamType) streamValue.getType(); - - return streamType.getConstrainedType() == targetType.getConstrainedType(); - } + return isInherentlyImmutableType(recordRestType) || + isSelectivelyImmutableType(recordRestType, unresolvedTypes); + case TypeTags.OBJECT_TYPE_TAG: + BObjectType objectType = (BObjectType) type; - private static boolean checkIsLikeJSONType(Object sourceValue, Type sourceType, BJsonType targetType, - List unresolvedValues, boolean allowNumericConversion) { - Type referredSourceType = getImpliedType(sourceType); - switch (referredSourceType.getTag()) { - case TypeTags.ARRAY_TAG: - ArrayValue source = (ArrayValue) sourceValue; - Type elementType = ((BArrayType) referredSourceType).getElementType(); - if (checkIsType(elementType, targetType, new ArrayList<>())) { - return true; + if (SymbolFlags.isFlagOn(objectType.flags, SymbolFlags.CLASS) && + !SymbolFlags.isFlagOn(objectType.flags, SymbolFlags.READONLY)) { + return false; } - Object[] arrayValues = source.getValues(); - for (int i = 0; i < source.size(); i++) { - if (!checkIsLikeType(null, arrayValues[i], targetType, unresolvedValues, - allowNumericConversion, null)) { - return false; - } - } - return true; - case TypeTags.TUPLE_TAG: - for (Object obj : ((TupleValueImpl) sourceValue).getValues()) { - if (!checkIsLikeType(null, obj, targetType, unresolvedValues, allowNumericConversion, - null)) { + for (Field field : objectType.getFields().values()) { + Type fieldType = field.getFieldType(); + if (!isInherentlyImmutableType(fieldType) && + !isSelectivelyImmutableType(fieldType, unresolvedTypes)) { return false; } } return true; case TypeTags.MAP_TAG: - return checkIsMappingLikeJsonType((MapValueImpl) sourceValue, targetType, unresolvedValues, - allowNumericConversion); - case TypeTags.RECORD_TYPE_TAG: - TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); - if (unresolvedValues.contains(typeValuePair)) { - return true; + Type constraintType = ((MapType) type).getConstrainedType(); + return isInherentlyImmutableType(constraintType) || + isSelectivelyImmutableType(constraintType, unresolvedTypes); + case TypeTags.TABLE_TAG: + Type tableConstraintType = ((BTableType) type).getConstrainedType(); + return isInherentlyImmutableType(tableConstraintType) || + isSelectivelyImmutableType(tableConstraintType, unresolvedTypes); + case TypeTags.UNION_TAG: + boolean readonlyIntersectionExists = false; + for (Type memberType : ((BUnionType) type).getMemberTypes()) { + if (isInherentlyImmutableType(memberType) || + isSelectivelyImmutableType(memberType, unresolvedTypes)) { + readonlyIntersectionExists = true; + break; + } } - unresolvedValues.add(typeValuePair); - return checkIsMappingLikeJsonType((MapValueImpl) sourceValue, targetType, unresolvedValues, - allowNumericConversion); + return readonlyIntersectionExists; + case TypeTags.INTERSECTION_TAG: + return isSelectivelyImmutableType(((BIntersectionType) type).getEffectiveType(), unresolvedTypes); + case TypeTags.TYPE_REFERENCED_TYPE_TAG: + return isSelectivelyImmutableType(((BTypeReferenceType) type).getReferredType(), unresolvedTypes); default: return false; } } - private static boolean checkIsMappingLikeJsonType(MapValueImpl sourceValue, BJsonType targetType, - List unresolvedValues, - boolean allowNumericConversion) { - for (Object value : sourceValue.values()) { - if (!checkIsLikeType(null, value, targetType, unresolvedValues, allowNumericConversion, - null)) { - return false; - } - } - return true; + static boolean isSigned32LiteralValue(Long longObject) { + + return (longObject >= SIGNED32_MIN_VALUE && longObject <= SIGNED32_MAX_VALUE); } - private static boolean checkIsLikeRecordType(Object sourceValue, BRecordType targetType, - List unresolvedValues, boolean allowNumericConversion, - String varName, List errors) { - if (!(sourceValue instanceof MapValueImpl sourceMapValue)) { - return false; - } + static boolean isSigned16LiteralValue(Long longObject) { - TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); - if (unresolvedValues.contains(typeValuePair)) { - return true; - } - unresolvedValues.add(typeValuePair); + return (longObject.intValue() >= SIGNED16_MIN_VALUE && longObject.intValue() <= SIGNED16_MAX_VALUE); + } - Map targetFieldTypes = new HashMap<>(); - Type restFieldType = targetType.restFieldType; - boolean returnVal = true; + static boolean isSigned8LiteralValue(Long longObject) { - for (Field field : targetType.getFields().values()) { - targetFieldTypes.put(field.getFieldName(), field.getFieldType()); - } + return (longObject.intValue() >= SIGNED8_MIN_VALUE && longObject.intValue() <= SIGNED8_MAX_VALUE); + } - for (Map.Entry targetTypeEntry : targetFieldTypes.entrySet()) { - String fieldName = targetTypeEntry.getKey().toString(); - String fieldNameLong = TypeConverter.getLongFieldName(varName, fieldName); - Field targetField = targetType.getFields().get(fieldName); + static boolean isUnsigned32LiteralValue(Long longObject) { - if (!(sourceMapValue.containsKey(StringUtils.fromString(fieldName))) && - !SymbolFlags.isFlagOn(targetField.getFlags(), SymbolFlags.OPTIONAL)) { - addErrorMessage((errors == null) ? 0 : errors.size(), "missing required field '" + fieldNameLong + - "' of type '" + targetField.getFieldType().toString() + "' in record '" + targetType + "'", - errors); - if ((errors == null) || (errors.size() >= MAX_TYPECAST_ERROR_COUNT + 1)) { - return false; - } - returnVal = false; - } - } + return (longObject >= 0 && longObject <= UNSIGNED32_MAX_VALUE); + } - for (Object object : sourceMapValue.entrySet()) { - Map.Entry valueEntry = (Map.Entry) object; - String fieldName = valueEntry.getKey().toString(); - String fieldNameLong = TypeConverter.getLongFieldName(varName, fieldName); - int initialErrorCount = (errors == null) ? 0 : errors.size(); + static boolean isUnsigned16LiteralValue(Long longObject) { - if (targetFieldTypes.containsKey(fieldName)) { - if (!checkIsLikeType(errors, (valueEntry.getValue()), targetFieldTypes.get(fieldName), - unresolvedValues, allowNumericConversion, fieldNameLong)) { - addErrorMessage(initialErrorCount, "field '" + fieldNameLong + "' in record '" + targetType + - "' should be of type '" + targetFieldTypes.get(fieldName) + "', found '" + - TypeConverter.getShortSourceValue(valueEntry.getValue()) + "'", errors); - returnVal = false; - } - } else { - if (!targetType.sealed) { - if (!checkIsLikeType(errors, (valueEntry.getValue()), restFieldType, unresolvedValues, - allowNumericConversion, fieldNameLong)) { - addErrorMessage(initialErrorCount, "value of field '" + valueEntry.getKey() + - "' adding to the record '" + targetType + "' should be of type '" + restFieldType + - "', found '" + TypeConverter.getShortSourceValue(valueEntry.getValue()) + "'", errors); - returnVal = false; - } - } else { - addErrorMessage(initialErrorCount, "field '" + fieldNameLong + - "' cannot be added to the closed record '" + targetType + "'", errors); - returnVal = false; - } - } - if ((!returnVal) && ((errors == null) || (errors.size() >= MAX_TYPECAST_ERROR_COUNT + 1))) { - return false; - } - } - return returnVal; + return (longObject.intValue() >= 0 && longObject.intValue() <= UNSIGNED16_MAX_VALUE); } - private static void addErrorMessage(int initialErrorCount, String errorMessage, List errors) { - if ((errors != null) && (errors.size() <= MAX_TYPECAST_ERROR_COUNT) && - ((errors.size() - initialErrorCount) == 0)) { - errors.add(errorMessage); - } - } + static boolean isUnsigned8LiteralValue(Long longObject) { - private static boolean checkIsLikeTableType(Object sourceValue, BTableType targetType, - List unresolvedValues, boolean allowNumericConversion) { - if (!(sourceValue instanceof TableValueImpl tableValue)) { - return false; - } - BTableType sourceType = (BTableType) getImpliedType(tableValue.getType()); - if (targetType.getKeyType() != null && sourceType.getFieldNames().length == 0) { - return false; - } + return (longObject.intValue() >= 0 && longObject.intValue() <= UNSIGNED8_MAX_VALUE); + } - if (sourceType.getKeyType() != null && !checkIsType(tableValue.getKeyType(), targetType.getKeyType())) { + static boolean isCharLiteralValue(Object object) { + String value; + if (object instanceof BString) { + value = ((BString) object).getValue(); + } else if (object instanceof String) { + value = (String) object; + } else { return false; } + return value.codePoints().count() == 1; + } - TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); - if (unresolvedValues.contains(typeValuePair)) { + /** + * Deep value equality check for anydata. + * + * @param lhsValue The value on the left hand side + * @param rhsValue The value on the right hand side + * @param checkedValues Structured value pairs already compared or being compared + * @return True if values are equal, else false. + */ + public static boolean isEqual(Object lhsValue, Object rhsValue, Set checkedValues) { + if (lhsValue == rhsValue) { return true; } - Object[] objects = tableValue.values().toArray(); - for (Object object : objects) { - if (!checkIsLikeType(object, targetType.getConstrainedType(), allowNumericConversion)) { - return false; - } + if (null == lhsValue || null == rhsValue) { + return false; } - return true; + + return checkValueEqual(lhsValue, rhsValue, new HashSet<>(checkedValues)); } - private static boolean checkFiniteTypeAssignable(Object sourceValue, Type sourceType, BFiniteType targetType, - List unresolvedValues, - boolean allowNumericConversion) { - if (targetType.valueSpace.size() == 1) { - Type valueType = getImpliedType(getType(targetType.valueSpace.iterator().next())); - if (!isSimpleBasicType(valueType) && valueType.getTag() != TypeTags.NULL_TAG) { - return checkIsLikeOnValue(null, sourceValue, sourceType, valueType, unresolvedValues, - allowNumericConversion, null); - } + private static boolean checkValueEqual(Object lhsValue, Object rhsValue, Set checkedValues) { + Context cx = context(); + SemType lhsShape = ShapeAnalyzer.inherentTypeOf(cx, lhsValue).orElseThrow(); + SemType rhsShape = ShapeAnalyzer.inherentTypeOf(cx, rhsValue).orElseThrow(); + Predicate belongToSameBasicType = (basicType) -> Core.containsBasicType(lhsShape, basicType) && + Core.containsBasicType(rhsShape, basicType); + if (belongToSameBasicType.test(Builder.getStringType()) || + belongToSameBasicType.test(Builder.getBooleanType())) { + return lhsValue.equals(rhsValue); + } + if (belongToSameBasicType.test(Builder.getIntType())) { + // TODO: is this correct if one of the values are bytes (shouldn't we check of unsigned etc) + return ((Number) lhsValue).longValue() == ((Number) rhsValue).longValue(); } + if (belongToSameBasicType.test(Builder.getFloatType())) { + Double lhs = (Double) lhsValue; + Double rhs = (Double) rhsValue; + // directly doing equals don't work with -0 and 0 + return (Double.isNaN(lhs) && Double.isNaN(rhs)) || lhs.doubleValue() == rhs.doubleValue(); + } + if (belongToSameBasicType.test(Builder.getDecimalType())) { + return checkDecimalEqual((DecimalValue) lhsValue, (DecimalValue) rhsValue); + } + if (belongToSameBasicType.test(RefValueTypeMaskHolder.REF_TYPE_MASK)) { + RefValue lhs = (RefValue) lhsValue; + return lhs.equals(rhsValue, checkedValues); + } + return false; + } - for (Object valueSpaceItem : targetType.valueSpace) { - // TODO: 8/13/19 Maryam fix for conversion - if (isFiniteTypeValue(sourceValue, sourceType, valueSpaceItem, allowNumericConversion)) { + public static boolean isRegExpType(Type targetType) { + if (targetType.getTag() == TypeTags.TYPE_REFERENCED_TYPE_TAG) { + Type referredType = ((BTypeReferenceType) targetType).getReferredType(); + Module referredTypePackage = referredType.getPackage(); + if ((referredTypePackage != null) && BALLERINA_BUILTIN_PKG_PREFIX.equals(referredTypePackage.getOrg()) + && REGEXP_LANG_LIB.equals(referredTypePackage.getName()) + && REG_EXP_TYPENAME.equals(referredType.getName())) { return true; } + return isRegExpType(referredType); } return false; } + static boolean isStructuredType(Type type) { + Type referredType = getImpliedType(type); + return switch (referredType.getTag()) { + case TypeTags.ARRAY_TAG, + TypeTags.TUPLE_TAG, + TypeTags.MAP_TAG, + TypeTags.RECORD_TYPE_TAG, + TypeTags.TABLE_TAG -> + true; + default -> false; + }; + } + static boolean isFiniteTypeValue(Object sourceValue, Type sourceType, Object valueSpaceItem, boolean allowNumericConversion) { Type valueSpaceItemType = getType(valueSpaceItem); @@ -2808,7 +918,8 @@ static boolean isFiniteTypeValue(Object sourceValue, Type sourceType, Object val DecimalValue.valueOf(((Number) valueSpaceItem).longValue())) && allowNumericConversion; case TypeTags.FLOAT_TAG: return checkDecimalEqual((DecimalValue) sourceValue, - DecimalValue.valueOf(((Number) valueSpaceItem).doubleValue())) && allowNumericConversion; + DecimalValue.valueOf(((Number) valueSpaceItem).doubleValue())) && + allowNumericConversion; case TypeTags.DECIMAL_TAG: return checkDecimalEqual((DecimalValue) sourceValue, (DecimalValue) valueSpaceItem); } @@ -2820,183 +931,8 @@ static boolean isFiniteTypeValue(Object sourceValue, Type sourceType, Object val } } - private static boolean checkIsErrorType(Type sourceType, BErrorType targetType, List unresolvedTypes) { - if (sourceType.getTag() != TypeTags.ERROR_TAG) { - return false; - } - // Handle recursive error types. - TypePair pair = new TypePair(sourceType, targetType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - BErrorType bErrorType = (BErrorType) sourceType; - - if (!checkIsType(bErrorType.detailType, targetType.detailType, unresolvedTypes)) { - return false; - } - - if (targetType.typeIdSet == null) { - return true; - } - - BTypeIdSet sourceTypeIdSet = bErrorType.typeIdSet; - if (sourceTypeIdSet == null) { - return false; - } - - return sourceTypeIdSet.containsAll(targetType.typeIdSet); - } - - private static boolean checkIsLikeErrorType(Object sourceValue, BErrorType targetType, - List unresolvedValues, boolean allowNumericConversion) { - Type sourceTypeReferredType = getImpliedType(getType(sourceValue)); - if (sourceValue == null || sourceTypeReferredType.getTag() != TypeTags.ERROR_TAG) { - return false; - } - if (!checkIsLikeType(null, ((ErrorValue) sourceValue).getDetails(), targetType.detailType, - unresolvedValues, allowNumericConversion, null)) { - return false; - } - if (targetType.typeIdSet == null) { - return true; - } - BTypeIdSet sourceIdSet = ((BErrorType) sourceTypeReferredType).typeIdSet; - if (sourceIdSet == null) { - return false; - } - return sourceIdSet.containsAll(targetType.typeIdSet); - } - - static boolean isSimpleBasicType(Type type) { - return getImpliedType(type).getTag() < TypeTags.NULL_TAG; - } - - /** - * Deep value equality check for anydata. - * - * @param lhsValue The value on the left hand side - * @param rhsValue The value on the right hand side - * @param checkedValues Structured value pairs already compared or being compared - * @return True if values are equal, else false. - */ - public static boolean isEqual(Object lhsValue, Object rhsValue, Set checkedValues) { - if (lhsValue == rhsValue) { - return true; - } - - if (null == lhsValue || null == rhsValue) { - return false; - } - - return checkValueEquals(lhsValue, rhsValue, checkedValues, getType(lhsValue), getType(rhsValue)); - } - - private static boolean checkValueEquals(Object lhsValue, Object rhsValue, Set checkedValues, - Type lhsValType, Type rhsValType) { - lhsValType = getImpliedType(lhsValType); - rhsValType = getImpliedType(rhsValType); - int lhsValTypeTag = lhsValType.getTag(); - int rhsValTypeTag = rhsValType.getTag(); - - switch (lhsValTypeTag) { - case TypeTags.STRING_TAG: - case TypeTags.BOOLEAN_TAG: - return lhsValue.equals(rhsValue); - case TypeTags.INT_TAG: - if (rhsValTypeTag != TypeTags.BYTE_TAG && rhsValTypeTag != TypeTags.INT_TAG) { - return false; - } - return lhsValue.equals(((Number) rhsValue).longValue()); - case TypeTags.BYTE_TAG: - if (rhsValTypeTag != TypeTags.BYTE_TAG && rhsValTypeTag != TypeTags.INT_TAG) { - return false; - } - return ((Number) lhsValue).byteValue() == ((Number) rhsValue).byteValue(); - case TypeTags.FLOAT_TAG: - if (rhsValTypeTag != TypeTags.FLOAT_TAG) { - return false; - } - if (Double.isNaN((Double) lhsValue) && Double.isNaN((Double) rhsValue)) { - return true; - } - return ((Number) lhsValue).doubleValue() == ((Number) rhsValue).doubleValue(); - case TypeTags.DECIMAL_TAG: - if (rhsValTypeTag != TypeTags.DECIMAL_TAG) { - return false; - } - return checkDecimalEqual((DecimalValue) lhsValue, (DecimalValue) rhsValue); - case TypeTags.XML_TAG: - // Instance of xml never - if (lhsValue instanceof XmlText xmlText) { - return TypeTags.isXMLTypeTag(rhsValTypeTag) && xmlText.equals(rhsValue, checkedValues); - } - return TypeTags.isXMLTypeTag(rhsValTypeTag) && ((XmlSequence) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.XML_ELEMENT_TAG: - return TypeTags.isXMLTypeTag(rhsValTypeTag) && ((XmlItem) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.XML_COMMENT_TAG: - return TypeTags.isXMLTypeTag(rhsValTypeTag) && ((XmlComment) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.XML_TEXT_TAG: - return TypeTags.isXMLTypeTag(rhsValTypeTag) && ((XmlText) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.XML_PI_TAG: - return TypeTags.isXMLTypeTag(rhsValTypeTag) && ((XmlPi) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.MAP_TAG: - case TypeTags.JSON_TAG: - case TypeTags.RECORD_TYPE_TAG: - return isMappingType(rhsValTypeTag) && ((MapValueImpl) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.TUPLE_TAG: - case TypeTags.ARRAY_TAG: - return isListType(rhsValTypeTag) && ((ArrayValue) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.ERROR_TAG: - return rhsValTypeTag == TypeTags.ERROR_TAG && ((ErrorValue) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.TABLE_TAG: - return rhsValTypeTag == TypeTags.TABLE_TAG && - ((TableValueImpl) lhsValue).equals(rhsValue, checkedValues); - case TypeTags.TYPE_REFERENCED_TYPE_TAG: - return checkValueEquals(lhsValue, rhsValue, checkedValues, - ((BTypeReferenceType) lhsValType).getReferredType(), rhsValType); - case TypeTags.SERVICE_TAG: - default: - if (lhsValue instanceof RegExpValue lhsRegExpValue) { - return lhsRegExpValue.equals(rhsValue, checkedValues); - } - return false; - } - } - - private static boolean isListType(int typeTag) { - return typeTag == TypeTags.ARRAY_TAG || typeTag == TypeTags.TUPLE_TAG; - } - - private static boolean isMappingType(int typeTag) { - return typeTag == TypeTags.MAP_TAG || typeTag == TypeTags.RECORD_TYPE_TAG || typeTag == TypeTags.JSON_TAG; - } - - public static boolean isRegExpType(Type targetType) { - if (targetType.getTag() == TypeTags.TYPE_REFERENCED_TYPE_TAG) { - Type referredType = ((BTypeReferenceType) targetType).getReferredType(); - Module referredTypePackage = referredType.getPackage(); - if ((referredTypePackage != null) && BALLERINA_BUILTIN_PKG_PREFIX.equals(referredTypePackage.getOrg()) - && REGEXP_LANG_LIB.equals(referredTypePackage.getName()) - && REG_EXP_TYPENAME.equals(referredType.getName())) { - return true; - } - return isRegExpType(referredType); - } - return false; - } - - static boolean isStructuredType(Type type) { - Type referredType = getImpliedType(type); - return switch (referredType.getTag()) { - case TypeTags.ARRAY_TAG, - TypeTags.TUPLE_TAG, - TypeTags.MAP_TAG, - TypeTags.RECORD_TYPE_TAG, - TypeTags.TABLE_TAG -> - true; - default -> false; - }; + public static Env getEnv() { + return Env.getInstance(); } /** @@ -3004,7 +940,7 @@ static boolean isStructuredType(Type type) { * * @since 0.995.0 */ - private static class TypePair { + static class TypePair { Type sourceType; Type targetType; @@ -3044,11 +980,35 @@ public static boolean hasFillerValue(Type type) { return hasFillerValue(type, new ArrayList<>()); } + private enum FillerValueResult { + TRUE, FALSE, MAYBE + } + + private static FillerValueResult hasFillerValueSemType(Context cx, SemType type) { + if (Core.containsBasicType(type, Builder.getNilType())) { + return FillerValueResult.TRUE; + } + if (Integer.bitCount(type.all() | type.some()) > 1) { + return FillerValueResult.FALSE; + } + if (type.some() != 0) { + return FillerValueResult.MAYBE; + } + return Core.containsBasicType(type, TopTypesWithFillValueMaskHolder.TOP_TYPES_WITH_ALWAYS_FILLING) ? + FillerValueResult.TRUE : + FillerValueResult.FALSE; + } + private static boolean hasFillerValue(Type type, List unanalyzedTypes) { if (type == null) { return true; } + FillerValueResult fastResult = hasFillerValueSemType(context(), SemType.tryInto(type)); + if (fastResult != FillerValueResult.MAYBE) { + return fastResult == FillerValueResult.TRUE; + } + int typeTag = type.getTag(); if (TypeTags.isXMLTypeTag(typeTag)) { return typeTag == TypeTags.XML_TAG || typeTag == TypeTags.XML_TEXT_TAG; @@ -3077,7 +1037,7 @@ private static boolean hasFillerValue(Type type, List unanalyzedTypes) { }; } - private static boolean checkFillerValue(BTupleType tupleType, List unAnalyzedTypes) { + private static boolean checkFillerValue(BTupleType tupleType, List unAnalyzedTypes) { if (unAnalyzedTypes.contains(tupleType)) { return true; } @@ -3174,7 +1134,7 @@ private static boolean isSameBasicType(Type sourceType, Type targetType) { } private static boolean isIntegerSubTypeTag(int typeTag) { - return TypeTags.isIntegerTypeTag(typeTag) || typeTag == TypeTags.BYTE_TAG; + return TypeTags.isIntegerTypeTag(typeTag); } private static boolean isFillerValueOfFiniteTypeBasicType(Object value) { @@ -3253,7 +1213,8 @@ private static boolean hasFillerValueInValueSpace(Set finiteTypeValueSpa if (firstElement instanceof BString) { return containsElement(finiteTypeValueSpace, ""); - } else if ((firstElement instanceof Long) || (firstElement instanceof BDecimal)) { + } else if ((firstElement instanceof Integer) || (firstElement instanceof Long) || + (firstElement instanceof BDecimal)) { return containsElement(finiteTypeValueSpace, "0"); } else if (firstElement instanceof Double) { return containsElement(finiteTypeValueSpace, "0.0"); @@ -3289,6 +1250,96 @@ private static BError createTypeCastError(Object value, Type targetType, List + ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_BYTE)); + } + Predicate isIntSubType = (subType) -> Core.isSameType(cx, targetType, SemType.tryInto(subType)); + if (isIntSubType.test(PredefinedTypes.TYPE_INT_SIGNED_32)) { + return anyToSigned32(inputValue); + } + if (isIntSubType.test(PredefinedTypes.TYPE_INT_SIGNED_16)) { + return anyToSigned16(inputValue); + } + if (isIntSubType.test(PredefinedTypes.TYPE_INT_SIGNED_8)) { + return anyToSigned8(inputValue); + } + if (isIntSubType.test(PredefinedTypes.TYPE_INT_UNSIGNED_32)) { + return anyToUnsigned32(inputValue); + } + if (isIntSubType.test(PredefinedTypes.TYPE_INT_UNSIGNED_16)) { + return anyToUnsigned16(inputValue); + } + if (isIntSubType.test(PredefinedTypes.TYPE_INT_UNSIGNED_8)) { + return anyToUnsigned8(inputValue); + } + return anyToIntCast(inputValue, () -> + ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_INT)); + } + public static Object castValues(Type targetType, Object inputValue) { - return switch (targetType.getTag()) { - case TypeTags.SIGNED32_INT_TAG -> anyToSigned32(inputValue); - case TypeTags.SIGNED16_INT_TAG -> anyToSigned16(inputValue); - case TypeTags.SIGNED8_INT_TAG -> anyToSigned8(inputValue); - case TypeTags.UNSIGNED32_INT_TAG -> anyToUnsigned32(inputValue); - case TypeTags.UNSIGNED16_INT_TAG -> anyToUnsigned16(inputValue); - case TypeTags.UNSIGNED8_INT_TAG -> anyToUnsigned8(inputValue); - case TypeTags.INT_TAG -> anyToIntCast(inputValue, () -> - ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_INT)); - case TypeTags.DECIMAL_TAG -> anyToDecimalCast(inputValue, () -> + return castValuesInner(SemType.tryInto(targetType), inputValue, + () -> ErrorUtils.createTypeCastError(inputValue, targetType)); + } + + static Object castValuesInner(SemType targetType, Object inputValue, Supplier errorSupplier) { + Context cx = TypeChecker.context(); + if (Core.isSubType(cx, targetType, Builder.getIntType())) { + return castValueToInt(targetType, inputValue); + } + if (Core.isSubType(cx, targetType, Builder.getDecimalType())) { + return anyToDecimalCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_DECIMAL)); - case TypeTags.FLOAT_TAG -> anyToFloatCast(inputValue, () -> + } + if (Core.isSubType(cx, targetType, Builder.getFloatType())) { + return anyToFloatCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_FLOAT)); - case TypeTags.STRING_TAG -> anyToStringCast(inputValue, () -> + } + if (Core.isSubType(cx, targetType, Builder.getStringType())) { + return anyToStringCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_STRING)); - case TypeTags.BOOLEAN_TAG -> anyToBooleanCast(inputValue, () -> + } + if (Core.isSubType(cx, targetType, Builder.getBooleanType())) { + return anyToBooleanCast(inputValue, () -> ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_BOOLEAN)); - case TypeTags.BYTE_TAG -> anyToByteCast(inputValue, () -> - ErrorUtils.createTypeCastError(inputValue, PredefinedTypes.TYPE_BYTE)); - default -> throw ErrorUtils.createTypeCastError(inputValue, targetType); - }; + } + throw errorSupplier.get(); } static boolean isConvertibleToByte(Object value) { @@ -266,7 +308,7 @@ public static Type getConvertibleType(Object inputValue, Type targetType, String } break; case TypeTags.MAP_TAG: - if (isConvertibleToMapType(inputValue, (BMapType) targetType, unresolvedValues, varName, errors, + if (isConvertibleToMapType(inputValue, (MapType) targetType, unresolvedValues, varName, errors, allowNumericConversion)) { return targetType; } @@ -385,7 +427,7 @@ public static Type getConvertibleFiniteType(Object inputValue, BFiniteType targe // only the first matching type is returned. if (targetFiniteType.valueSpace.size() == 1) { Type valueType = getType(targetFiniteType.valueSpace.iterator().next()); - if (!isSimpleBasicType(valueType) && valueType.getTag() != TypeTags.NULL_TAG) { + if (!belongToSingleBasicTypeOrString(valueType) && valueType.getTag() != TypeTags.NULL_TAG) { return getConvertibleType(inputValue, valueType, varName, unresolvedValues, errors, allowNumericConversion); } @@ -499,7 +541,7 @@ static String getShortSourceValue(Object sourceValue) { return "()"; } String sourceValueName = sourceValue.toString(); - if (TypeChecker.getType(sourceValue) == TYPE_STRING) { + if (TypeChecker.checkIsType(sourceValue, TYPE_STRING)) { sourceValueName = "\"" + sourceValueName + "\""; } if (sourceValueName.length() > MAX_DISPLAYED_SOURCE_VALUE_LENGTH) { @@ -577,7 +619,7 @@ private static boolean isConvertibleToTableType(Object sourceValue, BTableType t } } - private static boolean isConvertibleToMapType(Object sourceValue, BMapType targetType, + private static boolean isConvertibleToMapType(Object sourceValue, MapType targetType, Set unresolvedValues, String varName, List errors, boolean allowNumericConversion) { if (!(sourceValue instanceof MapValueImpl)) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/json/JsonInternalUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/json/JsonInternalUtils.java index 0fe77bbde9da..e77b4b4aa9fb 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/json/JsonInternalUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/json/JsonInternalUtils.java @@ -43,7 +43,6 @@ import io.ballerina.runtime.internal.types.BArrayType; import io.ballerina.runtime.internal.types.BFiniteType; import io.ballerina.runtime.internal.types.BJsonType; -import io.ballerina.runtime.internal.types.BMapType; import io.ballerina.runtime.internal.types.BStructureType; import io.ballerina.runtime.internal.types.BUnionType; import io.ballerina.runtime.internal.values.ArrayValue; @@ -62,6 +61,7 @@ import java.util.Map.Entry; import static io.ballerina.runtime.api.constants.RuntimeConstants.MAP_LANG_LIB; +import static io.ballerina.runtime.internal.TypeChecker.isByteLiteral; import static io.ballerina.runtime.internal.errors.ErrorReasons.INHERENT_TYPE_VIOLATION_ERROR_IDENTIFIER; import static io.ballerina.runtime.internal.errors.ErrorReasons.JSON_OPERATION_ERROR; import static io.ballerina.runtime.internal.errors.ErrorReasons.MAP_KEY_NOT_FOUND_ERROR; @@ -317,6 +317,8 @@ public static Object convertJSON(Object jsonValue, Type targetType) { targetType = TypeUtils.getImpliedType(targetType); Type matchingType; switch (targetType.getTag()) { + case TypeTags.BYTE_TAG: + return jsonNodeToByte(jsonValue); case TypeTags.INT_TAG: return jsonNodeToInt(jsonValue); case TypeTags.FLOAT_TAG: @@ -360,7 +362,7 @@ public static Object convertJSON(Object jsonValue, Type targetType) { case TypeTags.ARRAY_TAG: return convertJSONToBArray(jsonValue, (BArrayType) targetType); case TypeTags.MAP_TAG: - return jsonToMap(jsonValue, (BMapType) targetType); + return jsonToMap(jsonValue, (MapType) targetType); case TypeTags.NULL_TAG: if (jsonValue == null) { return null; @@ -563,12 +565,30 @@ public static BError createJsonConversionError(Throwable throwable, String prefi * @return BInteger value of the JSON, if its a integer or a long JSON node. Error, otherwise. */ private static long jsonNodeToInt(Object json) { - if (!(json instanceof Long l)) { + if (!(json instanceof Long || json instanceof Integer)) { throw ErrorHelper.getRuntimeException(ErrorCodes.INCOMPATIBLE_TYPE_FOR_CASTING_JSON, PredefinedTypes.TYPE_INT, getTypeName(json)); } - return l; + return ((Number) json).longValue(); + } + + /** + * Convert to byte. + * + * @param json node to be converted + * @return JSON value as a long, if the value is within byte range. Error, otherwise. + */ + private static long jsonNodeToByte(Object json) { + if (json instanceof Long || json instanceof Integer) { + long x = ((Number) json).longValue(); + if (isByteLiteral(x)) { + return x; + } + } + + throw ErrorHelper.getRuntimeException(ErrorCodes.INCOMPATIBLE_TYPE_FOR_CASTING_JSON, + PredefinedTypes.TYPE_BYTE, getTypeName(json)); } /** diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java index feb9d147bf7c..adb22bc0bdde 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnyType.java @@ -25,6 +25,10 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.RefValue; import java.util.Optional; @@ -34,11 +38,7 @@ * * @since 0.995.0 */ -public class BAnyType extends BType implements AnyType { - - private final boolean readonly; - private IntersectionType immutableType; - private IntersectionType intersectionType = null; +public final class BAnyType extends BSemTypeWrapper implements AnyType { /** * Create a {@code BAnyType} which represents the any type. @@ -46,58 +46,98 @@ public class BAnyType extends BType implements AnyType { * @param typeName string name of the type */ public BAnyType(String typeName, Module pkg, boolean readonly) { - super(typeName, pkg, RefValue.class); - this.readonly = readonly; - - if (!readonly) { - BAnyType immutableAnyType = new BAnyType(TypeConstants.READONLY_ANY_TNAME, pkg, true); - this.immutableType = new BIntersectionType(pkg, new Type[]{ this, PredefinedTypes.TYPE_READONLY}, - immutableAnyType, TypeFlags.asMask(TypeFlags.NILABLE), true); - } + super(new ConcurrentLazySupplier<>(() -> new BAnyTypeImpl(typeName, pkg, readonly)), + typeName, pkg, TypeTags.ANY_TAG, pickSemType(readonly)); } @Override - public V getZeroValue() { - return null; + public Optional getIntersectionType() { + return this.getbType().getIntersectionType(); } @Override - public V getEmptyValue() { - return null; + public void setIntersectionType(IntersectionType intersectionType) { + this.getbType().setIntersectionType(intersectionType); } @Override - public int getTag() { - return TypeTags.ANY_TAG; + public Type getReferredType() { + return this.getbType().getReferredType(); } @Override - public boolean isNilable() { - return true; + public IntersectionType getImmutableType() { + return this.getbType().getImmutableType(); } - @Override - public boolean isReadOnly() { - return this.readonly; - } + protected static final class BAnyTypeImpl extends BType implements AnyType { - @Override - public IntersectionType getImmutableType() { - return this.immutableType; - } + private final boolean readonly; + private IntersectionType immutableType; + private IntersectionType intersectionType = null; - @Override - public void setImmutableType(IntersectionType immutableType) { - this.immutableType = immutableType; - } + private BAnyTypeImpl(String typeName, Module pkg, boolean readonly) { + super(typeName, pkg, RefValue.class); + this.readonly = readonly; + + if (!readonly) { + BAnyType immutableAnyType = new BAnyType(TypeConstants.READONLY_ANY_TNAME, pkg, true); + this.immutableType = new BIntersectionType(pkg, new Type[]{this, PredefinedTypes.TYPE_READONLY}, + immutableAnyType, TypeFlags.asMask(TypeFlags.NILABLE), true); + } + } + + @Override + public V getZeroValue() { + return null; + } + + @Override + public V getEmptyValue() { + return null; + } + + @Override + public int getTag() { + return TypeTags.ANY_TAG; + } + + public boolean isNilable() { + return true; + } + + @Override + public boolean isReadOnly() { + return this.readonly; + } + + @Override + public IntersectionType getImmutableType() { + return this.immutableType; + } + + @Override + public void setImmutableType(IntersectionType immutableType) { + this.immutableType = immutableType; + } + + @Override + public Optional getIntersectionType() { + return this.intersectionType == null ? Optional.empty() : Optional.of(this.intersectionType); + } + + @Override + public void setIntersectionType(IntersectionType intersectionType) { + this.intersectionType = intersectionType; + } - @Override - public Optional getIntersectionType() { - return this.intersectionType == null ? Optional.empty() : Optional.of(this.intersectionType); } - @Override - public void setIntersectionType(IntersectionType intersectionType) { - this.intersectionType = intersectionType; + private static SemType pickSemType(boolean readonly) { + SemType semType = Builder.getAnyType(); + if (readonly) { + semType = Core.intersect(semType, Builder.getReadonlyType()); + } + return semType; } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java index 0bc38e99f4fb..71dcdc374f48 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BAnydataType.java @@ -24,6 +24,9 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.RefValue; /** @@ -86,4 +89,16 @@ public String toString() { } return super.toString(); } + + // TODO: this type don't have mutable parts so this should be a immutable + // semtype. But some things could depend on this being a union type descriptor + // as well (which has to be mutable) + @Override + public SemType createSemType() { + SemType semType = Builder.getAnyDataType(); + if (isReadOnly()) { + semType = Core.intersect(semType, Builder.getReadonlyType()); + } + return semType; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java index a399bcddf71a..07834065c87d 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BArrayType.java @@ -22,12 +22,26 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.values.AbstractArrayValue; import io.ballerina.runtime.internal.values.ArrayValue; import io.ballerina.runtime.internal.values.ArrayValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import java.util.Optional; +import java.util.Set; + +import static io.ballerina.runtime.api.types.semtype.Builder.getNeverType; +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; /** * {@code BArrayType} represents a type of an arrays in Ballerina. @@ -40,7 +54,9 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BArrayType extends BType implements ArrayType { +public class BArrayType extends BType implements ArrayType, TypeWithShape { + + private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; private Type elementType; private int dimensions = 1; private int size = -1; @@ -51,6 +67,8 @@ public class BArrayType extends BType implements ArrayType { private IntersectionType immutableType; private IntersectionType intersectionType = null; private int typeFlags; + private final DefinitionContainer defn = new DefinitionContainer<>(); + private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); public BArrayType(Type elementType) { this(elementType, false); } @@ -85,6 +103,9 @@ public BArrayType(int typeFlags, int size, boolean readonly, boolean hasFillerVa } public void setElementType(Type elementType, int dimensions, boolean elementRO) { + if (this.elementType != null) { + resetSemType(); + } this.elementType = readonly && !elementRO ? ReadOnlyUtils.getReadOnlyType(elementType) : elementType; this.dimensions = dimensions; } @@ -129,7 +150,7 @@ public int hashCode() { @Override public boolean equals(Object obj) { if (obj instanceof BArrayType other) { - if (other.state == ArrayState.CLOSED && this.size != other.size) { + if ((other.state == ArrayState.CLOSED || this.state == ArrayState.CLOSED) && this.size != other.size) { return false; } return this.elementType.equals(other.elementType) && this.readonly == other.readonly; @@ -204,4 +225,101 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public SemType createSemType() { + Env env = Env.getInstance(); + if (defn.isDefinitionReady()) { + return defn.getSemType(env); + } + var result = defn.trySetDefinition(ListDefinition::new); + if (!result.updated()) { + return defn.getSemType(env); + } + ListDefinition ld = result.definition(); + CellAtomicType.CellMutability mut = isReadOnly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + return getSemTypePart(env, ld, size, tryInto(getElementType()), mut); + } + + private SemType getSemTypePart(Env env, ListDefinition defn, int size, SemType elementType, + CellAtomicType.CellMutability mut) { + if (size == -1) { + return defn.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, elementType, mut); + } else { + SemType[] initial = {elementType}; + return defn.defineListTypeWrapped(env, initial, size, getNeverType(), mut); + } + } + + @Override + public void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return elementType instanceof MayBeDependentType eType && eType.isDependentlyTyped(visited); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + AbstractArrayValue value = (AbstractArrayValue) object; + SemType cachedShape = value.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + SemType semType = shapeOfInner(cx, shapeSupplier, value); + value.cacheShape(semType); + return Optional.of(semType); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return isReadOnly(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + return Optional.of(shapeOfInner(cx, shapeSupplier, (AbstractArrayValue) object)); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn.isDefinitionReady()) { + return Optional.of(acceptedTypeDefn.getSemType(cx.env)); + } + var result = acceptedTypeDefn.trySetDefinition(ListDefinition::new); + if (!result.updated()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + ListDefinition ld = result.definition(); + SemType elementType = ShapeAnalyzer.acceptedTypeOf(cx, getElementType()).orElseThrow(); + return Optional.of(getSemTypePart(env, ld, size, elementType, CELL_MUT_UNLIMITED)); + } + + private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, AbstractArrayValue value) { + ListDefinition readonlyShapeDefinition = value.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition != null) { + return readonlyShapeDefinition.getSemType(cx.env); + } + int size = value.size(); + SemType[] memberTypes = new SemType[size]; + ListDefinition ld = new ListDefinition(); + value.setReadonlyShapeDefinition(ld); + for (int i = 0; i < size; i++) { + Optional memberType = shapeSupplier.get(cx, value.get(i)); + assert memberType.isPresent(); + memberTypes[i] = memberType.get(); + } + CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : CELL_MUT_LIMITED; + SemType semType = ld.defineListTypeWrapped(cx.env, memberTypes, memberTypes.length, getNeverType(), mut); + value.resetReadonlyShapeDefinition(); + return semType; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java index 6fd5b0afb07d..f5a6c275ccda 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BBooleanType.java @@ -18,15 +18,29 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.BooleanType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; /** * {@code BBooleanType} represents boolean type in Ballerina. * * @since 0.995.0 */ -public class BBooleanType extends BType implements BooleanType { +public final class BBooleanType extends BSemTypeWrapper implements BooleanType { + + private static final BBooleanType TRUE = + new BBooleanType(() -> new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), + TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.getBooleanConst(true)); + private static final BBooleanType FALSE = + new BBooleanType(() -> new BBooleanTypeImpl(TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE), + TypeConstants.BOOLEAN_TNAME, PredefinedTypes.EMPTY_MODULE, Builder.getBooleanConst(false)); /** * Create a {@code BBooleanType} which represents the boolean type. @@ -34,28 +48,43 @@ public class BBooleanType extends BType implements BooleanType { * @param typeName string name of the type */ public BBooleanType(String typeName, Module pkg) { - super(typeName, pkg, Boolean.class); + this(() -> new BBooleanTypeImpl(typeName, pkg), typeName, pkg, Builder.getBooleanType()); } - @Override - @SuppressWarnings("unchecked") - public V getZeroValue() { - return (V) Boolean.FALSE; + public static BBooleanType singletonType(boolean value) { + return value ? TRUE : FALSE; } - @SuppressWarnings("unchecked") - @Override - public V getEmptyValue() { - return (V) Boolean.FALSE; + private BBooleanType(Supplier bTypeSupplier, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, pkg, TypeTags.BOOLEAN_TAG, semType); } - @Override - public int getTag() { - return TypeTags.BOOLEAN_TAG; - } + protected static final class BBooleanTypeImpl extends BType implements BooleanType { + + private BBooleanTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, Boolean.class); + } + + @Override + @SuppressWarnings("unchecked") + public V getZeroValue() { + return (V) Boolean.FALSE; + } + + @SuppressWarnings("unchecked") + @Override + public V getEmptyValue() { + return (V) Boolean.FALSE; + } + + @Override + public int getTag() { + return TypeTags.BOOLEAN_TAG; + } - @Override - public boolean isReadOnly() { - return true; + @Override + public boolean isReadOnly() { + return true; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java index 97aca3f82894..d69b43dc9b8d 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BByteType.java @@ -19,15 +19,26 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.ByteType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; + +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED8_MAX_VALUE; +import static io.ballerina.runtime.api.types.PredefinedTypes.EMPTY_MODULE; /** * {@code BByteType} represents byte type in Ballerina. * * @since 0.995.0 */ -public class BByteType extends BType implements ByteType { +public final class BByteType extends BSemTypeWrapper implements ByteType { + + private static final BByteTypeImpl DEFAULT_B_TYPE = new BByteTypeImpl(TypeConstants.BYTE_TNAME, EMPTY_MODULE); /** * Create a {@code BByteType} which represents the byte type. @@ -35,28 +46,50 @@ public class BByteType extends BType implements ByteType { * @param typeName string name of the type */ public BByteType(String typeName, Module pkg) { - super(typeName, pkg, Integer.class); + this(() -> new BByteTypeImpl(typeName, pkg), typeName, EMPTY_MODULE, + Builder.createIntRange(0, UNSIGNED8_MAX_VALUE)); } - @Override - @SuppressWarnings("unchecked") - public V getZeroValue() { - return (V) new Integer(0); + private BByteType(Supplier bTypeSupplier, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, pkg, TypeTags.BYTE_TAG, semType); } - @Override - @SuppressWarnings("unchecked") - public V getEmptyValue() { - return (V) new Integer(0); + public static BByteType singletonType(long value) { + return new BByteType(() -> (BByteTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.BYTE_TNAME, EMPTY_MODULE, + Builder.getIntConst(value)); } - @Override - public int getTag() { - return TypeTags.BYTE_TAG; - } + protected static final class BByteTypeImpl extends BType implements ByteType, Cloneable { + + private BByteTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, Integer.class); + } + + @Override + @SuppressWarnings("unchecked") + public V getZeroValue() { + return (V) new Integer(0); + } + + @Override + @SuppressWarnings("unchecked") + public V getEmptyValue() { + return (V) new Integer(0); + } + + @Override + public int getTag() { + return TypeTags.BYTE_TAG; + } + + @Override + public boolean isReadOnly() { + return true; + } - @Override - public boolean isReadOnly() { - return true; + @Override + public BType clone() { + return super.clone(); + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java index d1bf252d0f0b..441da73583d2 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BDecimalType.java @@ -19,11 +19,18 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.DecimalType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.values.DecimalValue; import java.math.BigDecimal; +import java.util.function.Supplier; + +import static io.ballerina.runtime.api.types.PredefinedTypes.EMPTY_MODULE; /** * {@code BDecimalType} represents decimal type in Ballerina. @@ -31,7 +38,10 @@ * * @since 0.995.0 */ -public class BDecimalType extends BType implements DecimalType { +public final class BDecimalType extends BSemTypeWrapper implements DecimalType { + + private static final BDecimalTypeImpl DEFAULT_B_TYPE = + new BDecimalTypeImpl(TypeConstants.DECIMAL_TNAME, EMPTY_MODULE); /** * Create a {@code BDecimalType} which represents the decimal type. @@ -39,28 +49,49 @@ public class BDecimalType extends BType implements DecimalType { * @param typeName string name of the type */ public BDecimalType(String typeName, Module pkg) { - super(typeName, pkg, DecimalValue.class); + this(() -> new BDecimalTypeImpl(typeName, pkg), typeName, pkg, Builder.getDecimalType()); } - @Override - @SuppressWarnings("unchecked") - public V getZeroValue() { - return (V) new DecimalValue(BigDecimal.ZERO); + public static BDecimalType singletonType(BigDecimal value) { + return new BDecimalType(() -> (BDecimalTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.DECIMAL_TNAME, + EMPTY_MODULE, Builder.getDecimalConst(value)); } - @Override - @SuppressWarnings("unchecked") - public V getEmptyValue() { - return (V) new DecimalValue(BigDecimal.ZERO); + private BDecimalType(Supplier bType, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bType), typeName, pkg, TypeTags.DECIMAL_TAG, semType); } - @Override - public int getTag() { - return TypeTags.DECIMAL_TAG; - } + protected static final class BDecimalTypeImpl extends BType implements DecimalType, Cloneable { + + private BDecimalTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, DecimalValue.class); + } + + @Override + @SuppressWarnings("unchecked") + public V getZeroValue() { + return (V) new DecimalValue(BigDecimal.ZERO); + } + + @Override + @SuppressWarnings("unchecked") + public V getEmptyValue() { + return (V) new DecimalValue(BigDecimal.ZERO); + } + + @Override + public int getTag() { + return TypeTags.DECIMAL_TAG; + } + + @Override + public boolean isReadOnly() { + return true; + } - @Override - public boolean isReadOnly() { - return true; + @Override + public BType clone() { + return super.clone(); + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java index 8c1002def149..44e8d987ccbf 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BErrorType.java @@ -24,20 +24,30 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +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.values.BError; +import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.ErrorUtils; import io.ballerina.runtime.internal.values.ErrorValue; +import io.ballerina.runtime.internal.values.MapValueImpl; import java.util.Optional; +import java.util.Set; /** * {@code BErrorType} represents error type in Ballerina. * * @since 0.995.0 */ -public class BErrorType extends BAnnotatableType implements ErrorType { +public class BErrorType extends BAnnotatableType implements ErrorType, TypeWithShape { - public Type detailType = PredefinedTypes.TYPE_ERROR_DETAIL; + public Type detailType = PredefinedTypes.TYPE_DETAIL; public BTypeIdSet typeIdSet; private IntersectionType intersectionType = null; + private volatile DistinctIdSupplier distinctIdSupplier; public BErrorType(String typeName, Module pkg, Type detailType) { super(typeName, pkg, ErrorValue.class); @@ -50,6 +60,9 @@ public BErrorType(String typeName, Module pkg) { public void setTypeIdSet(BTypeIdSet typeIdSet) { this.typeIdSet = typeIdSet; + synchronized (this) { + this.distinctIdSupplier = null; + } } @Override @@ -113,4 +126,76 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public SemType createSemType() { + SemType err; + if (detailType == null || isTopType()) { + err = Builder.getErrorType(); + } else { + err = ErrorUtils.errorDetail(tryInto(getDetailType())); + } + + initializeDistinctIdSupplierIfNeeded(); + return distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(err, Core::intersect); + } + + private void initializeDistinctIdSupplierIfNeeded() { + if (distinctIdSupplier == null) { + synchronized (this) { + if (distinctIdSupplier == null) { + distinctIdSupplier = new DistinctIdSupplier(TypeChecker.context().env, getTypeIdSet()); + } + } + } + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return detailType instanceof MayBeDependentType mayBeDependentType && + mayBeDependentType.isDependentlyTyped(visited); + } + + private boolean isTopType() { + return detailType == PredefinedTypes.TYPE_DETAIL; + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + BError errorValue = (BError) object; + Object details = errorValue.getDetails(); + if (!(details instanceof MapValueImpl errorDetails)) { + return Optional.empty(); + } + initializeDistinctIdSupplierIfNeeded(); + // Should we actually pass the readonly shape supplier here? + return BMapType.shapeOfInner(cx, shapeSupplier, errorDetails) + .map(ErrorUtils::errorDetail) + .map(err -> distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct) + .reduce(err, Core::intersect)); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + BError errorValue = (BError) object; + Object details = errorValue.getDetails(); + if (!(details instanceof MapValueImpl errorDetails)) { + return Optional.empty(); + } + return BMapType.shapeOfInner(cx, shapeSupplierFn, errorDetails).map(ErrorUtils::errorDetail); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + return Optional.of(getSemType()); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + // TODO: consider properly handling this + return true; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java index de03a587aa95..bcef259ed050 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFiniteType.java @@ -21,11 +21,17 @@ import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.FiniteType; import io.ballerina.runtime.api.types.TypeTags; +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.internal.TypeChecker; import io.ballerina.runtime.internal.values.RefValue; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.Optional; import java.util.Set; import java.util.StringJoiner; @@ -39,8 +45,8 @@ public class BFiniteType extends BType implements FiniteType { public Set valueSpace; - private final int typeFlags; - private final String originalName; + private int typeFlags; + private String originalName; public BFiniteType(String typeName) { this(typeName, new LinkedHashSet<>(), 0); @@ -187,6 +193,27 @@ public boolean equals(Object o) { if (!(o instanceof BFiniteType that)) { return false; } - return this.valueSpace.size() == that.valueSpace.size() && this.valueSpace.containsAll(that.valueSpace); + if (this.valueSpace.size() != that.valueSpace.size()) { + return false; + } + for (var each : this.valueSpace) { + try { + if (!that.valueSpace.contains(each)) { + return false; + } + } catch (NullPointerException ex) { + // If one of the sets is an immutable collection this can happen + return false; + } + } + return true; + } + + @Override + public SemType createSemType() { + Context cx = TypeChecker.context(); + return this.valueSpace.stream().map(each -> ShapeAnalyzer.inherentTypeOf(cx, each)) + .map(Optional::orElseThrow) + .reduce(Builder.getNeverType(), Core::union); } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java index 50450df52beb..7d5aab69a070 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFloatType.java @@ -18,8 +18,16 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.FloatType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; + +import static io.ballerina.runtime.api.types.PredefinedTypes.EMPTY_MODULE; /** * {@code BFloatType} represents a integer which is a 32-bit floating-point number according to the @@ -28,7 +36,7 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BFloatType extends BType implements FloatType { +public final class BFloatType extends BSemTypeWrapper implements FloatType { /** * Create a {@code BFloatType} which represents the boolean type. @@ -36,26 +44,42 @@ public class BFloatType extends BType implements FloatType { * @param typeName string name of the type */ public BFloatType(String typeName, Module pkg) { - super(typeName, pkg, Double.class); + this(() -> new BFloatTypeImpl(typeName, pkg), typeName, pkg, Builder.getFloatType()); } - @Override - public V getZeroValue() { - return (V) new Double(0); - } - - @Override - public V getEmptyValue() { - return (V) new Double(0); + private BFloatType(Supplier bType, String typeName, Module pkg, SemType semType) { + super(new ConcurrentLazySupplier<>(bType), typeName, pkg, TypeTags.FLOAT_TAG, semType); } - @Override - public int getTag() { - return TypeTags.FLOAT_TAG; + public static BFloatType singletonType(Double value) { + return new BFloatType(() -> new BFloatTypeImpl(TypeConstants.FLOAT_TNAME, EMPTY_MODULE), + TypeConstants.FLOAT_TNAME, EMPTY_MODULE, Builder.getFloatConst(value)); } - @Override - public boolean isReadOnly() { - return true; + protected static final class BFloatTypeImpl extends BType implements FloatType { + + private BFloatTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, Double.class); + } + + @Override + public V getZeroValue() { + return (V) new Double(0); + } + + @Override + public V getEmptyValue() { + return (V) new Double(0); + } + + @Override + public int getTag() { + return TypeTags.FLOAT_TAG; + } + + @Override + public boolean isReadOnly() { + return true; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java index dbef43081922..ecc2a44c1f98 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFunctionType.java @@ -25,8 +25,18 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.FunctionDefinition; +import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; import java.util.Arrays; +import java.util.Objects; +import java.util.Set; /** * {@code {@link BFunctionType }} represents a function type in ballerina. @@ -39,6 +49,10 @@ public class BFunctionType extends BAnnotatableType implements FunctionType { public Type retType; public long flags; public Parameter[] parameters; + private static final Env env = Env.getInstance(); + private static final SemType ISOLATED_TOP = createIsolatedTop(env); + + private final DefinitionContainer defn = new DefinitionContainer<>(); public BFunctionType(Module pkg) { super("function ()", pkg, Object.class); @@ -120,31 +134,14 @@ public boolean equals(Object o) { return false; } - boolean isSourceAnyFunction = SymbolFlags.isFlagOn(this.flags, SymbolFlags.ANY_FUNCTION); - boolean isTargetAnyFunction = SymbolFlags.isFlagOn(that.flags, SymbolFlags.ANY_FUNCTION); - - if (isSourceAnyFunction && isTargetAnyFunction) { - return true; - } - - if (isSourceAnyFunction != isTargetAnyFunction) { - return false; - } - - if (SymbolFlags.isFlagOn(that.flags, SymbolFlags.ISOLATED) != SymbolFlags - .isFlagOn(this.flags, SymbolFlags.ISOLATED)) { - return false; - } - - if (SymbolFlags.isFlagOn(that.flags, SymbolFlags.TRANSACTIONAL) != SymbolFlags - .isFlagOn(this.flags, SymbolFlags.TRANSACTIONAL)) { + if (this.flags != that.flags) { return false; } if (!Arrays.equals(parameters, that.parameters)) { return false; } - return retType.equals(that.retType); + return Objects.equals(retType, that.retType) && Objects.equals(restType, that.restType); } @Override @@ -218,4 +215,96 @@ public Type getReturnType() { public long getFlags() { return flags; } + + private static SemType createIsolatedTop(Env env) { + FunctionDefinition fd = new FunctionDefinition(); + SemType ret = Builder.getValType(); + return fd.define(env, Builder.getNeverType(), ret, FunctionQualifiers.create(true, false)); + } + + @Override + public SemType createSemType() { + if (isFunctionTop()) { + return getTopType(); + } + if (defn.isDefinitionReady()) { + return defn.getSemType(env); + } + var result = defn.trySetDefinition(FunctionDefinition::new); + if (!result.updated()) { + return defn.getSemType(env); + } + FunctionDefinition fd = result.definition(); + SemType[] params = new SemType[parameters.length]; + for (int i = 0; i < parameters.length; i++) { + params[i] = getSemType(parameters[i].type); + } + SemType rest; + if (restType instanceof BArrayType arrayType) { + rest = getSemType(arrayType.getElementType()); + } else { + rest = Builder.getNeverType(); + } + + SemType returnType = resolveReturnType(); + ListDefinition paramListDefinition = new ListDefinition(); + SemType paramType = paramListDefinition.defineListTypeWrapped(env, params, params.length, rest, + CellAtomicType.CellMutability.CELL_MUT_NONE); + return fd.define(env, paramType, returnType, getQualifiers()); + } + + private SemType getTopType() { + if (SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED)) { + return ISOLATED_TOP; + } + return Builder.getFunctionType(); + } + + FunctionQualifiers getQualifiers() { + return FunctionQualifiers.create(SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED), + SymbolFlags.isFlagOn(flags, SymbolFlags.TRANSACTIONAL)); + } + + private SemType getSemType(Type type) { + return tryInto(type); + } + + private boolean isFunctionTop() { + return parameters == null && restType == null && retType == null; + } + + @Override + public synchronized void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return (restType instanceof BType rest && rest.isDependentlyTyped(visited)) || + (retType instanceof BType ret && ret.isDependentlyTyped(visited)) || + isDependentlyTypeParameters(visited); + } + + private boolean isDependentlyTypeParameters(Set visited) { + if (parameters == null) { + return false; + } + return Arrays.stream(parameters).map(each -> each.type).filter(each -> each instanceof MayBeDependentType) + .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); + } + + private SemType resolveReturnType() { + if (retType == null) { + return Builder.getNilType(); + } + MayBeDependentType retBType = (MayBeDependentType) retType; + SemType returnType = getSemType(retType); + ListDefinition ld = new ListDefinition(); + SemType dependentlyTypedBit = + retBType.isDependentlyTyped() ? Builder.getBooleanConst(true) : Builder.getBooleanType(); + SemType[] innerType = new SemType[]{dependentlyTypedBit, returnType}; + return ld.defineListTypeWrapped(env, innerType, 2, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_NONE); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java index 3eb490648562..8839a92922fe 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BFutureType.java @@ -22,7 +22,13 @@ import io.ballerina.runtime.api.types.FutureType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.FutureUtils; + +import java.util.Objects; +import java.util.Set; /** * {@code BFutureType} represents a future value in Ballerina. @@ -31,7 +37,7 @@ */ public class BFutureType extends BType implements FutureType { - private Type constraint; + private final Type constraint; /** * Create a {@code {@link BFutureType}} which represents the future value. @@ -41,6 +47,7 @@ public class BFutureType extends BType implements FutureType { */ public BFutureType(String typeName, Module pkg) { super(typeName, pkg, Object.class); + constraint = null; } public BFutureType(Type constraint) { @@ -81,8 +88,7 @@ public boolean equals(Object obj) { if (constraint == other.constraint) { return true; } - - return TypeChecker.checkIsType(constraint, other.constraint); + return Objects.equals(constraint, other.constraint); } @Override @@ -93,4 +99,17 @@ public String toString() { private String getConstraintString() { return constraint != null ? "<" + constraint + ">" : ""; } + + @Override + public SemType createSemType() { + if (constraint == null) { + return Builder.getFutureType(); + } + return FutureUtils.futureContaining(TypeChecker.context().env, tryInto(constraint)); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return constraint instanceof MayBeDependentType constraintType && constraintType.isDependentlyTyped(visited); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java index ed1065c1df9e..9bd32efe9577 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BHandleType.java @@ -18,44 +18,68 @@ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.HandleType; +import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.internal.values.RefValue; /** - * {@code BHandleType} represents a handle type in Ballerina. - * A handle value is a reference to a storage managed externally by a Ballerina program. + * {@code BHandleType} represents a handle type in Ballerina. A handle value is a reference to a storage managed + * externally by a Ballerina program. * * @since 1.0.0 */ -public class BHandleType extends BType implements HandleType { +public final class BHandleType extends BSemTypeWrapper implements HandleType { /** - * Create a {@code BAnyType} which represents the any type. + * Create a {@code BHandleType} which represents the handle type. * * @param typeName string name of the type */ public BHandleType(String typeName, Module pkg) { - super(typeName, pkg, RefValue.class); + super(new ConcurrentLazySupplier<> + (() -> BHandleTypeImpl.create(typeName, pkg)), typeName, pkg, TypeTags.HANDLE_TAG, + Builder.getHandleType()); } - @Override - public V getZeroValue() { - return null; - } + protected static final class BHandleTypeImpl extends BType implements HandleType { - @Override - public V getEmptyValue() { - return null; - } + private static final BHandleTypeImpl DEFAULT = + new BHandleTypeImpl(TypeConstants.HANDLE_TNAME, PredefinedTypes.EMPTY_MODULE); - @Override - public int getTag() { - return TypeTags.HANDLE_TAG; - } + private static BHandleTypeImpl create(String typeName, Module pkg) { + if (typeName.equals(TypeConstants.HANDLE_TNAME) && pkg == PredefinedTypes.EMPTY_MODULE) { + return DEFAULT; + } + return new BHandleTypeImpl(typeName, pkg); + } + + private BHandleTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, RefValue.class); + } + + @Override + public V getZeroValue() { + return null; + } + + @Override + public V getEmptyValue() { + return null; + } + + @Override + public int getTag() { + return TypeTags.HANDLE_TAG; + } + + @Override + public boolean isReadOnly() { + return true; + } - @Override - public boolean isReadOnly() { - return true; } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java index d8ec02af768b..0b7053f3c0d0 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntegerType.java @@ -1,25 +1,42 @@ /* -* Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ + * Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ package io.ballerina.runtime.internal.types; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.IntegerType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; + +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED16_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED16_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED32_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED32_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED8_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED8_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED16_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED32_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED8_MAX_VALUE; +import static io.ballerina.runtime.api.types.PredefinedTypes.EMPTY_MODULE; /** * {@code BIntegerType} represents an integer which is a 32-bit signed number. @@ -27,9 +44,10 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BIntegerType extends BType implements IntegerType { +public final class BIntegerType extends BSemTypeWrapper implements IntegerType { - private final int tag; + private static final BIntegerTypeImpl DEFAULT_B_TYPE = + new BIntegerTypeImpl(TypeConstants.INT_TNAME, EMPTY_MODULE, TypeTags.INT_TAG); /** * Create a {@code BIntegerType} which represents the boolean type. @@ -37,32 +55,89 @@ public class BIntegerType extends BType implements IntegerType { * @param typeName string name of the type */ public BIntegerType(String typeName, Module pkg) { - super(typeName, pkg, Long.class); - tag = TypeTags.INT_TAG; + this(() -> new BIntegerTypeImpl(typeName, pkg, TypeTags.INT_TAG), typeName, pkg, TypeTags.INT_TAG, + Builder.getIntType()); } public BIntegerType(String typeName, Module pkg, int tag) { - super(typeName, pkg, Long.class); - this.tag = tag; + this(() -> new BIntegerTypeImpl(typeName, pkg, tag), typeName, pkg, tag, pickSemType(tag)); + } + + private BIntegerType(Supplier bIntegerTypeSupplier, String typeName, Module pkg, int tag, + SemType semType) { + super(new ConcurrentLazySupplier<>(bIntegerTypeSupplier), typeName, pkg, tag, semType); + } + + private static SemType pickSemType(int tag) { + return switch (tag) { + case TypeTags.INT_TAG -> Builder.getIntType(); + case TypeTags.SIGNED8_INT_TAG -> Builder.createIntRange(SIGNED8_MIN_VALUE, SIGNED8_MAX_VALUE); + case TypeTags.SIGNED16_INT_TAG -> Builder.createIntRange(SIGNED16_MIN_VALUE, SIGNED16_MAX_VALUE); + case TypeTags.SIGNED32_INT_TAG -> Builder.createIntRange(SIGNED32_MIN_VALUE, SIGNED32_MAX_VALUE); + case TypeTags.UNSIGNED8_INT_TAG, TypeTags.BYTE_TAG -> Builder.createIntRange(0, UNSIGNED8_MAX_VALUE); + case TypeTags.UNSIGNED16_INT_TAG -> Builder.createIntRange(0, UNSIGNED16_MAX_VALUE); + case TypeTags.UNSIGNED32_INT_TAG -> Builder.createIntRange(0, UNSIGNED32_MAX_VALUE); + default -> throw new UnsupportedOperationException("Unexpected int tag"); + }; } - @Override - public V getZeroValue() { - return (V) new Long(0); + public static BIntegerType singletonType(long value) { + if (value >= IntegerTypeCache.CACHE_MIN_VALUE && value <= IntegerTypeCache.CACHE_MAX_VALUE) { + return IntegerTypeCache.cache[(int) value - IntegerTypeCache.CACHE_MIN_VALUE]; + } + return createSingletonType(value); } - @Override - public V getEmptyValue() { - return (V) new Long(0); + private static BIntegerType createSingletonType(long value) { + return new BIntegerType(() -> (BIntegerTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.INT_TNAME, EMPTY_MODULE, + TypeTags.INT_TAG, Builder.getIntConst(value)); } - @Override - public int getTag() { - return tag; + protected static final class BIntegerTypeImpl extends BType implements IntegerType, Cloneable { + + private final int tag; + + private BIntegerTypeImpl(String typeName, Module pkg, int tag) { + super(typeName, pkg, Long.class); + this.tag = tag; + } + + @Override + public V getZeroValue() { + return (V) new Long(0); + } + + @Override + public V getEmptyValue() { + return (V) new Long(0); + } + + @Override + public int getTag() { + return tag; + } + + @Override + public boolean isReadOnly() { + return true; + } + + @Override + public BType clone() { + return super.clone(); + } } - @Override - public boolean isReadOnly() { - return true; + private static final class IntegerTypeCache { + + private static final BIntegerType[] cache; + private static final int CACHE_MAX_VALUE = 127; + private static final int CACHE_MIN_VALUE = -128; + static { + cache = new BIntegerType[CACHE_MAX_VALUE - CACHE_MIN_VALUE + 1]; + for (int i = CACHE_MIN_VALUE; i <= CACHE_MAX_VALUE; i++) { + cache[i - CACHE_MIN_VALUE] = createSingletonType(i); + } + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java index f9cf98a079df..dc1991feb42c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BIntersectionType.java @@ -24,20 +24,32 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +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.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.ErrorUtils; +import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; import java.util.StringJoiner; +import java.util.function.Function; + +import static io.ballerina.runtime.api.utils.TypeUtils.getImpliedType; /** * {@code BIntersectionType} represents an intersection type in Ballerina. * * @since 2.0.0 */ -public class BIntersectionType extends BType implements IntersectionType { +public class BIntersectionType extends BType implements IntersectionType, TypeWithShape { private static final String PADDED_AMPERSAND = " & "; private static final String OPENING_PARENTHESIS = "("; @@ -215,4 +227,71 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public SemType createSemType() { + return createSemTypeInner(SemType::tryInto); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return constituentTypes.stream().filter(each -> each instanceof MayBeDependentType) + .anyMatch(type -> ((MayBeDependentType) type).isDependentlyTyped(visited)); + } + + private SemType createSemTypeInner(Function semTypeFunction) { + if (constituentTypes.isEmpty()) { + return Builder.getNeverType(); + } + SemType result = constituentTypes.stream().map(semTypeFunction).reduce(Core::intersect).orElseThrow(); + Optional distinctPart = distinctTypePart(result); + if (distinctPart.isPresent()) { + result = Core.intersect(result, distinctPart.get()); + } + return result; + } + + private Optional distinctTypePart(SemType result) { + if (Core.isSubtypeSimple(result, Builder.getErrorType())) { + BErrorType effectiveErrorType = (BErrorType) getImpliedType(effectiveType); + DistinctIdSupplier distinctIdSupplier = + new DistinctIdSupplier(TypeChecker.context().env, effectiveErrorType.getTypeIdSet()); + return distinctIdSupplier.get().stream().map(ErrorUtils::errorDistinct).reduce(Core::intersect); + } else if (Core.isSubtypeSimple(result, Builder.getObjectType())) { + BObjectType effectiveObjectType = (BObjectType) getImpliedType(effectiveType); + DistinctIdSupplier distinctIdSupplier = + new DistinctIdSupplier(TypeChecker.context().env, effectiveObjectType.getTypeIdSet()); + return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(Core::intersect); + } + return Optional.empty(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + Type effectiveType = getEffectiveType(); + if (effectiveType instanceof TypeWithShape typeWithShape) { + return typeWithShape.shapeOf(cx, shapeSupplierFn, object); + } + return Optional.empty(); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + return Optional.of(createSemTypeInner(type -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow())); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + Type effectiveType = getEffectiveType(); + if (effectiveType instanceof TypeWithShape typeWithShape) { + return typeWithShape.inherentTypeOf(cx, shapeSupplier, object); + } + return Optional.empty(); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return true; + } + } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java index fa47a26e06fc..43d2058cd4d9 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMapType.java @@ -24,11 +24,24 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; +import java.util.Map; import java.util.Optional; +import java.util.Set; + +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; /** * {@code BMapType} represents a type of a map in Ballerina. @@ -41,12 +54,15 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BMapType extends BType implements MapType { +public class BMapType extends BType implements MapType, TypeWithShape, Cloneable { + public static final MappingDefinition.Field[] EMPTY_FIELD_ARR = new MappingDefinition.Field[0]; private final Type constraint; private final boolean readonly; private IntersectionType immutableType; private IntersectionType intersectionType = null; + private final DefinitionContainer defn = new DefinitionContainer<>(); + private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); public BMapType(Type constraint) { this(constraint, false); @@ -169,4 +185,104 @@ public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + @Override + public SemType createSemType() { + Env env = Env.getInstance(); + if (defn.isDefinitionReady()) { + return defn.getSemType(env); + } + var result = defn.trySetDefinition(MappingDefinition::new); + if (!result.updated()) { + return defn.getSemType(env); + } + MappingDefinition md = result.definition(); + CellAtomicType.CellMutability mut = isReadOnly() ? CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + return createSemTypeInner(env, md, tryInto(getConstrainedType()), mut); + } + + @Override + public void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + MapValueImpl value = (MapValueImpl) object; + SemType cachedShape = value.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + + return shapeOfInner(cx, shapeSupplier, value); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return isReadOnly(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return shapeOfInner(cx, shapeSupplierFn, (MapValueImpl) object); + } + + @Override + public synchronized Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn.isDefinitionReady()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + var result = acceptedTypeDefn.trySetDefinition(MappingDefinition::new); + if (!result.updated()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + MappingDefinition md = result.definition(); + SemType elementType = ShapeAnalyzer.acceptedTypeOf(cx, getConstrainedType()).orElseThrow(); + return Optional.of(createSemTypeInner(env, md, elementType, CELL_MUT_UNLIMITED)); + } + + static Optional shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueImpl value) { + MappingDefinition readonlyShapeDefinition = value.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition != null) { + return Optional.of(readonlyShapeDefinition.getSemType(cx.env)); + } + int nFields = value.size(); + MappingDefinition md = new MappingDefinition(); + value.setReadonlyShapeDefinition(md); + MappingDefinition.Field[] fields = new MappingDefinition.Field[nFields]; + Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); + for (int i = 0; i < nFields; i++) { + Optional valueType = shapeSupplier.get(cx, entries[i].getValue()); + SemType fieldType = valueType.orElseThrow(); + fields[i] = new MappingDefinition.Field(entries[i].getKey().toString(), fieldType, true, false); + } + CellAtomicType.CellMutability mut = value.getType().isReadOnly() ? CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + SemType semType = md.defineMappingTypeWrapped(cx.env, fields, Builder.getNeverType(), mut); + value.cacheShape(semType); + value.resetReadonlyShapeDefinition(); + return Optional.of(semType); + } + + private SemType createSemTypeInner(Env env, MappingDefinition defn, SemType restType, + CellAtomicType.CellMutability mut) { + return defn.defineMappingTypeWrapped(env, EMPTY_FIELD_ARR, restType, mut); + } + + @Override + public BMapType clone() { + BMapType clone = (BMapType) super.clone(); + clone.defn.clear(); + return clone; + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return constraint instanceof MayBeDependentType constraintType && constraintType.isDependentlyTyped(visited); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java index 46d25842338c..4332e1a2f49b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BMethodType.java @@ -84,4 +84,8 @@ public MethodType duplicate() { public boolean isIsolated() { return SymbolFlags.isFlagOn(flags, SymbolFlags.ISOLATED); } + + public String name() { + return funcName; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java index c289e0bffd88..5478813f2dc3 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNetworkObjectType.java @@ -24,6 +24,9 @@ import io.ballerina.runtime.api.types.ResourceMethodType; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.stream.Stream; /** * {@code BNetworkObjectType} represents a network object in Ballerina. @@ -79,4 +82,20 @@ private RemoteMethodType[] getRemoteMethods(MethodType[] methodTypes) { public ResourceMethodType[] getResourceMethods() { return resourceMethods; } + + @Override + protected Collection allMethods() { + Stream methodStream = Arrays.stream(getMethods()) + .filter(methodType -> !(SymbolFlags.isFlagOn(methodType.getFlags(), SymbolFlags.REMOTE) || + SymbolFlags.isFlagOn(methodType.getFlags(), SymbolFlags.RESOURCE))) + .map(MethodData::fromMethod); + Stream remoteMethodStream = + Arrays.stream(getRemoteMethods()) + .map(MethodData::fromRemoteMethod); + Stream resourceMethodStream = + Arrays.stream(getResourceMethods()) + .map(method -> MethodData.fromResourceMethod( + (BResourceMethodType) method)); + return Stream.concat(methodStream, Stream.concat(remoteMethodStream, resourceMethodStream)).toList(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNeverType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNeverType.java index f57eedcc6dcf..299b860ca917 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNeverType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNeverType.java @@ -21,29 +21,25 @@ import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.NeverType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; /** * {@code BNeverType} represents the type of a {@code Never}. * * @since 2.0.0-preview1 */ -public class BNeverType extends BNullType implements NeverType { +public final class BNeverType extends BNullType implements NeverType { /** * Create a {@code BNeverType} represents the type of a {@code Never}. * * @param pkg package path */ public BNeverType(Module pkg) { - super(TypeConstants.NEVER_TNAME, pkg); + super(TypeConstants.NEVER_TNAME, pkg, Builder.getNeverType(), TypeTags.NEVER_TAG); } @Override public boolean isAnydata() { return true; } - - @Override - public int getTag() { - return TypeTags.NEVER_TAG; - } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java index 9070784980fc..02e846517c71 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BNullType.java @@ -20,13 +20,18 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.NullType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; /** * {@code BNullType} represents the type of a {@code NullLiteral}. * * @since 0.995.0 */ -public class BNullType extends BType implements NullType { +public sealed class BNullType extends BSemTypeWrapper implements NullType permits BNeverType { /** * Create a {@code BNullType} represents the type of a {@code NullLiteral}. @@ -35,31 +40,47 @@ public class BNullType extends BType implements NullType { * @param pkg package path */ public BNullType(String typeName, Module pkg) { - super(typeName, pkg, null); + this(() -> new BNullTypeImpl(typeName, pkg), typeName, pkg, TypeTags.NULL_TAG, Builder.getNilType()); } - @Override - public V getZeroValue() { - return null; + protected BNullType(String typeName, Module pkg, SemType semType, int tag) { + this(() -> new BNullTypeImpl(typeName, pkg), typeName, pkg, tag, semType); } - @Override - public V getEmptyValue() { - return null; + private BNullType(Supplier bNullTypeSupplier, String typeName, Module pkg, int tag, + SemType semType) { + super(new ConcurrentLazySupplier<>(bNullTypeSupplier), typeName, pkg, tag, semType); } - @Override - public int getTag() { - return TypeTags.NULL_TAG; - } + protected static final class BNullTypeImpl extends BType implements NullType { - @Override - public boolean isNilable() { - return true; - } + private BNullTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, null); + } + + @Override + public V getZeroValue() { + return null; + } + + @Override + public V getEmptyValue() { + return null; + } + + @Override + public int getTag() { + return TypeTags.NULL_TAG; + } + + @Override + public boolean isNilable() { + return true; + } - @Override - public boolean isReadOnly() { - return true; + @Override + public boolean isReadOnly() { + return true; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java index 05cd15e570fc..713a96f0d469 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BObjectType.java @@ -22,25 +22,49 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.FunctionType; import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ObjectType; +import io.ballerina.runtime.api.types.Parameter; import io.ballerina.runtime.api.types.ResourceMethodType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeIdSet; import io.ballerina.runtime.api.types.TypeTags; +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.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BObject; +import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.scheduling.Scheduler; import io.ballerina.runtime.internal.scheduling.Strand; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.FunctionDefinition; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.types.semtype.Member; +import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; +import io.ballerina.runtime.internal.types.semtype.ObjectQualifiers; import io.ballerina.runtime.internal.utils.ValueUtils; +import io.ballerina.runtime.internal.values.AbstractObjectValue; import java.lang.reflect.Array; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.Set; import java.util.StringJoiner; +import java.util.function.Function; import static io.ballerina.runtime.api.types.TypeTags.SERVICE_TAG; @@ -49,7 +73,7 @@ * * @since 0.995.0 */ -public class BObjectType extends BStructureType implements ObjectType { +public class BObjectType extends BStructureType implements ObjectType, TypeWithShape { private MethodType[] methodTypes; private MethodType initMethod; @@ -62,6 +86,9 @@ public class BObjectType extends BStructureType implements ObjectType { private String cachedToString; private boolean resolving; + private final DefinitionContainer defn = new DefinitionContainer<>(); + private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); + private volatile DistinctIdSupplier distinctIdSupplier; /** * Create a {@code BObjectType} which represents the user defined struct type. @@ -215,6 +242,10 @@ public void setIntersectionType(IntersectionType intersectionType) { public void setTypeIdSet(BTypeIdSet typeIdSet) { this.typeIdSet = typeIdSet; + synchronized (this) { + this.distinctIdSupplier = null; + } + resetSemType(); } public BObjectType duplicate() { @@ -243,6 +274,270 @@ public boolean hasAnnotations() { @Override public TypeIdSet getTypeIdSet() { + if (typeIdSet == null) { + return new BTypeIdSet(); + } return new BTypeIdSet(new ArrayList<>(typeIdSet.ids)); } + + @Override + public final SemType createSemType() { + Env env = Env.getInstance(); + initializeDistinctIdSupplierIfNeeded(env); + CellAtomicType.CellMutability mut = + SymbolFlags.isFlagOn(getFlags(), SymbolFlags.READONLY) ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + SemType innerType; + if (defn.isDefinitionReady()) { + innerType = defn.getSemType(env); + } else { + var result = defn.trySetDefinition(ObjectDefinition::new); + if (!result.updated()) { + innerType = defn.getSemType(env); + } else { + ObjectDefinition od = result.definition(); + innerType = semTypeInner(od, mut, SemType::tryInto); + } + } + return distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(innerType, Core::intersect); + } + + private static boolean skipField(Set seen, String name) { + if (name.startsWith("$")) { + return true; + } + return !seen.add(name); + } + + private SemType semTypeInner(ObjectDefinition od, CellAtomicType.CellMutability mut, + Function semTypeSupplier) { + Env env = Env.getInstance(); + ObjectQualifiers qualifiers = getObjectQualifiers(); + List members = new ArrayList<>(); + Set seen = new HashSet<>(); + for (Entry entry : fields.entrySet()) { + String name = entry.getKey(); + if (skipField(seen, name)) { + continue; + } + Field field = entry.getValue(); + boolean isPublic = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.PUBLIC); + boolean isImmutable = qualifiers.readonly() | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); + members.add(new Member(name, semTypeSupplier.apply(field.getFieldType()), Member.Kind.Field, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); + } + for (MethodData method : allMethods()) { + String name = method.name(); + if (skipField(seen, name)) { + continue; + } + boolean isPublic = SymbolFlags.isFlagOn(method.flags(), SymbolFlags.PUBLIC); + members.add(new Member(name, method.semType(), Member.Kind.Method, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); + } + return od.define(env, qualifiers, members, mut); + } + + private ObjectQualifiers getObjectQualifiers() { + boolean isolated = SymbolFlags.isFlagOn(getFlags(), SymbolFlags.ISOLATED); + boolean readonly = SymbolFlags.isFlagOn(getFlags(), SymbolFlags.READONLY); + ObjectQualifiers.NetworkQualifier networkQualifier; + if (SymbolFlags.isFlagOn(getFlags(), SymbolFlags.SERVICE)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Service; + } else if (SymbolFlags.isFlagOn(getFlags(), SymbolFlags.CLIENT)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Client; + } else { + networkQualifier = ObjectQualifiers.NetworkQualifier.None; + } + return new ObjectQualifiers(isolated, readonly, networkQualifier); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + AbstractObjectValue abstractObjectValue = (AbstractObjectValue) object; + SemType cachedShape = abstractObjectValue.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + initializeDistinctIdSupplierIfNeeded(cx.env); + SemType shape = distinctIdSupplier.get().stream().map(ObjectDefinition::distinct) + .reduce(valueShape(cx, shapeSupplier, abstractObjectValue), Core::intersect); + abstractObjectValue.cacheShape(shape); + return Optional.of(shape); + } + + private void initializeDistinctIdSupplierIfNeeded(Env env) { + if (distinctIdSupplier == null) { + synchronized (this) { + if (distinctIdSupplier == null) { + distinctIdSupplier = new DistinctIdSupplier(env, typeIdSet); + } + } + } + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return Optional.of(valueShape(cx, shapeSupplierFn, (AbstractObjectValue) object)); + } + + @Override + public final Optional acceptedTypeOf(Context cx) { + Env env = Env.getInstance(); + initializeDistinctIdSupplierIfNeeded(cx.env); + CellAtomicType.CellMutability mut = CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; + SemType innerType; + if (acceptedTypeDefn.isDefinitionReady()) { + innerType = acceptedTypeDefn.getSemType(env); + } else { + var result = acceptedTypeDefn.trySetDefinition(ObjectDefinition::new); + if (!result.updated()) { + innerType = acceptedTypeDefn.getSemType(env); + } else { + ObjectDefinition od = result.definition(); + innerType = semTypeInner(od, mut, (type -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow())); + } + } + return Optional.of( + distinctIdSupplier.get().stream().map(ObjectDefinition::distinct).reduce(innerType, Core::intersect)); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + if (SymbolFlags.isFlagOn(getFlags(), SymbolFlags.READONLY)) { + return true; + } + return fields.values().stream().anyMatch( + field -> SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY) || + SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.FINAL)); + } + + private SemType valueShape(Context cx, ShapeSupplier shapeSupplier, AbstractObjectValue object) { + ObjectDefinition readonlyShapeDefinition = object.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition != null) { + return readonlyShapeDefinition.getSemType(cx.env); + } + ObjectDefinition od = new ObjectDefinition(); + object.setReadonlyShapeDefinition(od); + List members = new ArrayList<>(); + Set seen = new HashSet<>(fields.size() + methodTypes.length); + ObjectQualifiers qualifiers = getObjectQualifiers(); + for (Entry entry : fields.entrySet()) { + String name = entry.getKey(); + if (skipField(seen, name)) { + continue; + } + Field field = entry.getValue(); + boolean isPublic = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.PUBLIC); + boolean isImmutable = qualifiers.readonly() | SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY) | + SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.FINAL); + members.add(new Member(name, fieldShape(cx, shapeSupplier, field, object, isImmutable), Member.Kind.Field, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, isImmutable)); + } + for (MethodData method : allMethods()) { + String name = method.name(); + if (skipField(seen, name)) { + continue; + } + boolean isPublic = SymbolFlags.isFlagOn(method.flags(), SymbolFlags.PUBLIC); + members.add(new Member(name, method.semType(), Member.Kind.Method, + isPublic ? Member.Visibility.Public : Member.Visibility.Private, true)); + } + return od.define(cx.env, qualifiers, members, + qualifiers.readonly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + } + + private static SemType fieldShape(Context cx, ShapeSupplier shapeSupplier, Field field, + AbstractObjectValue objectValue, boolean isImmutable) { + if (!isImmutable) { + return SemType.tryInto(field.getFieldType()); + } + BString fieldName = StringUtils.fromString(field.getFieldName()); + Optional shape = shapeSupplier.get(cx, objectValue.get(fieldName)); + assert !shape.isEmpty(); + return shape.get(); + } + + @Override + public void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + protected Collection allMethods() { + if (methodTypes == null) { + return List.of(); + } + return Arrays.stream(methodTypes) + .map(MethodData::fromMethod).toList(); + } + + protected record MethodData(String name, long flags, SemType semType) { + + static MethodData fromMethod(MethodType method) { + return new MethodData(method.getName(), method.getFlags(), + tryInto(method.getType())); + } + + static MethodData fromRemoteMethod(MethodType method) { + // Remote methods need to be distinct with remote methods only there can be instance methods with the same + // name + return new MethodData("@remote_" + method.getName(), method.getFlags(), + tryInto(method.getType())); + } + + static MethodData fromResourceMethod(BResourceMethodType method) { + StringBuilder sb = new StringBuilder(); + sb.append(method.getAccessor()); + for (var each : method.getResourcePath()) { + sb.append(each); + } + String methodName = sb.toString(); + + Type[] pathSegmentTypes = method.pathSegmentTypes; + FunctionType innerFn = method.getType(); + List paramTypes = new ArrayList<>(); + for (Type part : pathSegmentTypes) { + if (part == null) { + paramTypes.add(Builder.getAnyType()); + } else { + paramTypes.add(tryInto(part)); + } + } + for (Parameter paramType : innerFn.getParameters()) { + paramTypes.add(tryInto(paramType.type)); + } + SemType rest; + Type restType = innerFn.getRestType(); + if (restType instanceof BArrayType arrayType) { + rest = tryInto(arrayType.getElementType()); + } else { + rest = Builder.getNeverType(); + } + + SemType returnType; + if (innerFn.getReturnType() != null) { + returnType = tryInto(innerFn.getReturnType()); + } else { + returnType = Builder.getNilType(); + } + ListDefinition paramListDefinition = new ListDefinition(); + Env env = TypeChecker.context().env; + SemType paramType = paramListDefinition.defineListTypeWrapped(env, paramTypes.toArray(SemType[]::new), + paramTypes.size(), rest, CellAtomicType.CellMutability.CELL_MUT_NONE); + FunctionDefinition fd = new FunctionDefinition(); + SemType semType = fd.define(env, paramType, returnType, ((BFunctionType) innerFn).getQualifiers()); + return new MethodData(methodName, method.getFlags(), semType); + } + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return fields.values().stream().map(Field::getFieldType).filter(each -> each instanceof MayBeDependentType) + .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java index 9ab78cf7271c..05b9229d68ac 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BParameterizedType.java @@ -21,6 +21,9 @@ import io.ballerina.runtime.api.types.ParameterizedType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Set; /** * {@code ParameterizedType} represents the parameterized type in dependently-typed functions. @@ -80,4 +83,14 @@ public Type getParamValueType() { public int getParamIndex() { return this.paramIndex; } + + @Override + public SemType createSemType() { + return SemType.tryInto(this.paramValueType); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return true; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java index c945f4941839..c4366334b3fa 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BReadonlyType.java @@ -20,6 +20,8 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.types.ReadonlyType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; import io.ballerina.runtime.internal.values.RefValue; /** @@ -27,34 +29,41 @@ * * @since 1.3.0 */ -public class BReadonlyType extends BType implements ReadonlyType { +public final class BReadonlyType extends BSemTypeWrapper implements ReadonlyType { public BReadonlyType(String typeName, Module pkg) { - super(typeName, pkg, RefValue.class); + super(new ConcurrentLazySupplier<>(() -> new BReadonlyTypeImpl(typeName, pkg)), typeName, pkg, + TypeTags.READONLY_TAG, Builder.getReadonlyType()); } - @Override - public V getZeroValue() { - return null; - } + protected static final class BReadonlyTypeImpl extends BType implements ReadonlyType { - @Override - public V getEmptyValue() { - return null; - } + private BReadonlyTypeImpl(String typeName, Module pkg) { + super(typeName, pkg, RefValue.class); + } - @Override - public int getTag() { - return TypeTags.READONLY_TAG; - } + @Override + public V getZeroValue() { + return null; + } - @Override - public boolean isNilable() { - return true; - } + @Override + public V getEmptyValue() { + return null; + } + + @Override + public int getTag() { + return TypeTags.READONLY_TAG; + } + + public boolean isNilable() { + return true; + } - @Override - public boolean isReadOnly() { - return true; + @Override + public boolean isReadOnly() { + return true; + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java index 2af42d5596d2..f64e13db2304 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BRecordType.java @@ -20,6 +20,7 @@ import io.ballerina.identifier.Utils; import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.creators.TypeCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.flags.TypeFlags; @@ -28,26 +29,44 @@ import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +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.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BFunctionPointer; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.scheduling.Scheduler; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.values.MapValue; import io.ballerina.runtime.internal.values.MapValueImpl; import io.ballerina.runtime.internal.values.ReadOnlyUtils; +import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.function.Function; + +import static io.ballerina.runtime.api.types.semtype.Builder.getNeverType; +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; /** * {@code BRecordType} represents a user defined record type in Ballerina. * * @since 0.995.0 */ -public class BRecordType extends BStructureType implements RecordType { +public class BRecordType extends BStructureType implements RecordType, TypeWithShape { private final String internalName; public boolean sealed; public Type restFieldType; @@ -55,6 +74,9 @@ public class BRecordType extends BStructureType implements RecordType { private final boolean readonly; private IntersectionType immutableType; private IntersectionType intersectionType = null; + private final DefinitionContainer defn = new DefinitionContainer<>(); + private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); + private byte couldInhereTypeBeDifferentCache = 0; private final Map defaultValues = new LinkedHashMap<>(); @@ -73,6 +95,7 @@ public BRecordType(String typeName, String internalName, Module pkg, long flags, this.sealed = sealed; this.typeFlags = typeFlags; this.readonly = SymbolFlags.isFlagOn(flags, SymbolFlags.READONLY); + TypeCreator.registerRecordType(this); } /** @@ -102,6 +125,7 @@ public BRecordType(String typeName, Module pkg, long flags, Map f this.fields = fields; } this.internalName = typeName; + TypeCreator.registerRecordType(this); } private Map getReadOnlyFields(Map fields) { @@ -218,4 +242,190 @@ public Map getDefaultValues() { return defaultValues; } + @Override + public SemType createSemType() { + Env env = Env.getInstance(); + if (defn.isDefinitionReady()) { + return defn.getSemType(env); + } + var result = defn.trySetDefinition(MappingDefinition::new); + if (!result.updated()) { + return defn.getSemType(env); + } + MappingDefinition md = result.definition(); + return createSemTypeInner(md, env, mut(), SemType::tryInto); + } + + private CellMutability mut() { + return isReadOnly() ? CELL_MUT_NONE : CellMutability.CELL_MUT_LIMITED; + } + + private SemType createSemTypeInner(MappingDefinition md, Env env, CellMutability mut, + Function semTypeFunction) { + Field[] fields = getFields().values().toArray(Field[]::new); + MappingDefinition.Field[] mappingFields = new MappingDefinition.Field[fields.length]; + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + boolean isOptional = SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); + SemType fieldType = semTypeFunction.apply(field.getFieldType()); + if (!isOptional && Core.isNever(fieldType)) { + return getNeverType(); + } + boolean isReadonly = + SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY) || Core.isNever(fieldType); + mappingFields[i] = new MappingDefinition.Field(field.getFieldName(), fieldType, + isReadonly, isOptional); + } + SemType rest; + rest = restFieldType != null ? semTypeFunction.apply(restFieldType) : getNeverType(); + return md.defineMappingTypeWrapped(env, mappingFields, rest, mut); + } + + @Override + public void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + @Override + public boolean isDependentlyTypedInner(Set visited) { + return fields.values().stream().map(Field::getFieldType).filter(each -> each instanceof MayBeDependentType) + .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + MapValueImpl value = (MapValueImpl) object; + SemType cachedSemType = value.shapeOf(); + if (cachedSemType != null) { + return Optional.of(cachedSemType); + } + SemType semTypePart = shapeOfInner(cx, shapeSupplier, value, isReadOnly()); + value.cacheShape(semTypePart); + return Optional.of(semTypePart); + } + + private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, MapValueImpl value, + boolean takeFieldShape) { + Env env = cx.env; + int nFields = value.size(); + Map.Entry[] entries = value.entrySet().toArray(Map.Entry[]::new); + Set handledFields = new HashSet<>(nFields); + MappingDefinition md; + if (takeFieldShape) { + MappingDefinition readonlyShapeDefinition = value.getReadonlyShapeDefinition(); + if (readonlyShapeDefinition != null) { + return readonlyShapeDefinition.getSemType(env); + } else { + md = new MappingDefinition(); + value.setReadonlyShapeDefinition(md); + } + } else { + md = new MappingDefinition(); + } + List fields = new ArrayList<>(nFields); + for (int i = 0; i < nFields; i++) { + String fieldName = entries[i].getKey().toString(); + Object fieldValue = entries[i].getValue(); + handledFields.add(fieldName); + fields.add(fieldShape(cx, shapeSupplier, fieldName, fieldValue, takeFieldShape)); + } + if (!takeFieldShape) { + getFields().values().stream() + .filter(field -> !handledFields.contains(field.getFieldName())) + .map(field -> fieldShapeWithoutValue(field, field.getFieldName())) + .forEach(fields::add); + } + MappingDefinition.Field[] fieldsArray = fields.toArray(MappingDefinition.Field[]::new); + SemType rest; + if (takeFieldShape) { + rest = Builder.getNeverType(); + } else { + rest = restFieldType != null ? SemType.tryInto(restFieldType) : getNeverType(); + } + SemType shape = md.defineMappingTypeWrapped(env, fieldsArray, rest, mut()); + value.resetReadonlyShapeDefinition(); + return shape; + } + + private MappingDefinition.Field fieldShapeWithoutValue(Field field, String fieldName) { + boolean isOptional = fieldIsOptional(fieldName); + boolean isReadonly = fieldIsReadonly(fieldName); + SemType fieldType = SemType.tryInto(field.getFieldType()); + if (isReadonly && isOptional) { + fieldType = Builder.getUndefType(); + } + MappingDefinition.Field field1 = new MappingDefinition.Field(field.getFieldName(), fieldType, + isReadonly, isOptional); + return field1; + } + + @Override + public boolean couldInherentTypeBeDifferent() { + if (couldInhereTypeBeDifferentCache != 0) { + return couldInhereTypeBeDifferentCache == 1; + } + boolean result = couldShapeBeDifferentInner(); + couldInhereTypeBeDifferentCache = (byte) (result ? 1 : 2); + return result; + } + + private boolean couldShapeBeDifferentInner() { + if (isReadOnly()) { + return true; + } + return fields.values().stream().anyMatch(field -> SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY)); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + return Optional.of(shapeOfInner(cx, shapeSupplier, (MapValueImpl) object, true)); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn.isDefinitionReady()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + var result = acceptedTypeDefn.trySetDefinition(MappingDefinition::new); + if (!result.updated()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + MappingDefinition md = result.definition(); + return Optional.of(createSemTypeInner(md, env, CELL_MUT_UNLIMITED, + (type) -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow())); + } + + private Type fieldType(String fieldName) { + Field field = fields.get(fieldName); + return field == null ? restFieldType : field.getFieldType(); + } + + private boolean fieldIsReadonly(String fieldName) { + Field field = fields.get(fieldName); + return field != null && SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.READONLY); + } + + private boolean fieldIsOptional(String fieldName) { + Field field = fields.get(fieldName); + return field != null && SymbolFlags.isFlagOn(field.getFlags(), SymbolFlags.OPTIONAL); + } + + private MappingDefinition.Field fieldShape(Context cx, ShapeSupplier shapeSupplier, String fieldName, + Object fieldValue, boolean alwaysTakeValueShape) { + boolean readonlyField = fieldIsReadonly(fieldName); + boolean optionalField = fieldIsOptional(fieldName); + SemType fieldType; + if (alwaysTakeValueShape || readonlyField) { + optionalField = false; + fieldType = shapeSupplier.get(cx, fieldValue).orElseThrow(); + } else { + fieldType = SemType.tryInto(fieldType(fieldName)); + } + return new MappingDefinition.Field(fieldName, fieldType, readonlyField, optionalField); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java new file mode 100644 index 000000000000..4c1f5ed85391 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BSemTypeWrapper.java @@ -0,0 +1,206 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types; + +import io.ballerina.runtime.api.Module; +import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.Type; +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.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.ImmutableSemType; + +import java.util.Objects; +import java.util.Set; +import java.util.function.Supplier; + +/** + * Decorator on {@code BTypes} allowing them to behave as {@code SemType}. All + * {@code Types} that needs to behave as both a {@code BType} and a + * {@code SemType} should extend this class. + * + * @param The type of the {@code BType} that is being wrapped. + * @since 2201.11.0 + */ +public sealed class BSemTypeWrapper extends ImmutableSemType implements Type, MayBeDependentType + permits BAnyType, BBooleanType, BByteType, BDecimalType, BFloatType, BHandleType, BIntegerType, BNullType, + BReadonlyType, BStringType { + + private Type cachedReferredType = null; + private Type cachedImpliedType = null; + + private final Supplier bTypeSupplier; + private final int tag; + protected final String typeName; // Debugger uses this field to show the type name + private final Module pkg; + + protected BSemTypeWrapper(Supplier bTypeSupplier, String typeName, Module pkg, int tag, SemType semType) { + super(semType); + this.bTypeSupplier = bTypeSupplier; + this.typeName = typeName; + this.tag = tag; + this.pkg = pkg; + } + + public final Class getValueClass() { + return getbType().getValueClass(); + } + + @Override + public final V getZeroValue() { + return getbType().getZeroValue(); + } + + @Override + public final V getEmptyValue() { + return getbType().getEmptyValue(); + } + + @Override + public final int getTag() { + return tag; + } + + @Override + public final String toString() { + return getbType().toString(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof BSemTypeWrapper other)) { + return false; + } + return Objects.equals(this.typeName, other.typeName) && Objects.equals(this.pkg, other.pkg); + } + + @Override + public final boolean isNilable() { + return Core.containsBasicType(this, Builder.getNilType()); + } + + @Override + public final int hashCode() { + return Objects.hash(this.typeName, this.pkg); + } + + @Override + public String getName() { + return typeName == null ? "" : typeName; + } + + @Override + public String getQualifiedName() { + String name = getName(); + if (name.isEmpty()) { + return ""; + } + + return pkg == null ? name : pkg + ":" + name; + } + + @Override + public Module getPackage() { + return pkg; + } + + @Override + public boolean isPublic() { + return getbType().isPublic(); + } + + @Override + public boolean isNative() { + return getbType().isNative(); + } + + @Override + public boolean isAnydata() { + Context cx = TypeChecker.context(); + return Core.isSubType(cx, this, Builder.getAnyDataType()); + } + + @Override + public boolean isPureType() { + Context cx = TypeChecker.context(); + return Core.isSubType(cx, this, Builder.getErrorType()) || isAnydata(); + } + + @Override + public boolean isReadOnly() { + Context cx = TypeChecker.context(); + return Core.isSubType(cx, this, Builder.getReadonlyType()); + } + + @Override + public Type getImmutableType() { + return getbType().getImmutableType(); + } + + @Override + public void setImmutableType(IntersectionType immutableType) { + getbType().setImmutableType(immutableType); + } + + @Override + public Module getPkg() { + return pkg; + } + + @Override + public long getFlags() { + return getbType().getFlags(); + } + + @Override + public void setCachedReferredType(Type type) { + cachedReferredType = type; + } + + @Override + public Type getCachedReferredType() { + return cachedReferredType; + } + + @Override + public void setCachedImpliedType(Type type) { + cachedImpliedType = type; + } + + @Override + public Type getCachedImpliedType() { + return cachedImpliedType; + } + + protected E getbType() { + return bTypeSupplier.get(); + } + + @Override + public boolean isDependentlyTyped() { + return false; + } + + @Override + public boolean isDependentlyTyped(Set visited) { + return isDependentlyTyped(); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java index 494dc1d54642..1382a3e29943 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStreamType.java @@ -24,9 +24,16 @@ import io.ballerina.runtime.api.types.StreamType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.StreamDefinition; import io.ballerina.runtime.internal.values.StreamValue; import java.util.Objects; +import java.util.Set; /** * {@link BStreamType} represents streaming data in Ballerina. @@ -37,6 +44,7 @@ public class BStreamType extends BType implements StreamType { private final Type constraint; private final Type completionType; + private final DefinitionContainer definition = new DefinitionContainer<>(); /** * Creates a {@link BStreamType} which represents the stream type. @@ -135,4 +143,29 @@ public boolean equals(Object obj) { return Objects.equals(constraint, other.constraint) && Objects.equals(completionType, other.completionType); } + + @Override + public SemType createSemType() { + if (constraint == null) { + return Builder.getStreamType(); + } + Env env = TypeChecker.context().env; + if (definition.isDefinitionReady()) { + return definition.getSemType(env); + } + var result = definition.trySetDefinition(StreamDefinition::new); + if (!result.updated()) { + return definition.getSemType(env); + } + StreamDefinition sd = result.definition(); + return sd.define(env, tryInto(constraint), tryInto(completionType)); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return (constraint instanceof MayBeDependentType constrainedType && + constrainedType.isDependentlyTyped(visited)) || + (completionType instanceof MayBeDependentType completionType && + completionType.isDependentlyTyped(visited)); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java index 0211eef19b38..7ddbd3bf0f7c 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStringType.java @@ -19,8 +19,14 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.constants.RuntimeConstants; +import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.StringType; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.ConcurrentLazySupplier; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.function.Supplier; /** * {@code BStringType} represents a String type in ballerina. @@ -28,9 +34,13 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BStringType extends BType implements StringType { +public final class BStringType extends BSemTypeWrapper implements StringType { - private final int tag; + // We are creating separate empty module instead of reusing PredefinedTypes.EMPTY_MODULE to avoid cyclic + // dependencies. + private static final Module DEFAULT_MODULE = new Module(null, null, null); + private static final BStringTypeImpl DEFAULT_B_TYPE = + new BStringTypeImpl(TypeConstants.STRING_TNAME, DEFAULT_MODULE, TypeTags.STRING_TAG); /** * Create a {@code BStringType} which represents the boolean type. @@ -38,32 +48,64 @@ public class BStringType extends BType implements StringType { * @param typeName string name of the type */ public BStringType(String typeName, Module pkg) { - super(typeName, pkg, String.class); - tag = TypeTags.STRING_TAG; + this(() -> new BStringTypeImpl(typeName, pkg, TypeTags.STRING_TAG), typeName, pkg, TypeTags.STRING_TAG, + Builder.getStringType()); } public BStringType(String typeName, Module pkg, int tag) { - super(typeName, pkg, String.class); - this.tag = tag; + this(() -> new BStringTypeImpl(typeName, pkg, tag), typeName, pkg, tag, pickSemtype(tag)); } - @Override - public V getZeroValue() { - return (V) RuntimeConstants.STRING_EMPTY_VALUE; + private BStringType(Supplier bTypeSupplier, String typeName, Module pkg, int tag, + SemType semType) { + super(new ConcurrentLazySupplier<>(bTypeSupplier), typeName, pkg, tag, semType); } - @Override - public V getEmptyValue() { - return (V) RuntimeConstants.STRING_EMPTY_VALUE; + public static BStringType singletonType(String value) { + return new BStringType(() -> (BStringTypeImpl) DEFAULT_B_TYPE.clone(), TypeConstants.STRING_TNAME, + DEFAULT_MODULE, TypeTags.STRING_TAG, Builder.getStringConst(value)); } - @Override - public int getTag() { - return tag; + private static SemType pickSemtype(int tag) { + return switch (tag) { + case TypeTags.STRING_TAG -> Builder.getStringType(); + case TypeTags.CHAR_STRING_TAG -> Builder.getCharType(); + default -> throw new IllegalStateException("Unexpected string type tag: " + tag); + }; } - @Override - public boolean isReadOnly() { - return true; + protected static final class BStringTypeImpl extends BType implements StringType, Cloneable { + + private final int tag; + + private BStringTypeImpl(String typeName, Module pkg, int tag) { + super(typeName, pkg, String.class); + this.tag = tag; + } + + @Override + public V getZeroValue() { + return (V) RuntimeConstants.STRING_EMPTY_VALUE; + } + + @Override + public V getEmptyValue() { + return (V) RuntimeConstants.STRING_EMPTY_VALUE; + } + + @Override + public int getTag() { + return tag; + } + + @Override + public boolean isReadOnly() { + return true; + } + + @Override + public BType clone() { + return super.clone(); + } } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStructureType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStructureType.java index 64cf037ebde1..2acdcf181db7 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStructureType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BStructureType.java @@ -72,6 +72,7 @@ public Map getFields() { @Override public void setFields(Map fields) { this.fields = fields; + resetSemType(); } @Override 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 14c7d2b803e2..df743c54c344 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 @@ -22,18 +22,27 @@ import io.ballerina.runtime.api.types.TableType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +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.values.BTable; +import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.TableUtils; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TableValue; import io.ballerina.runtime.internal.values.TableValueImpl; import java.util.Optional; +import java.util.Set; /** * {@code BTableType} represents tabular data in Ballerina. * * @since 1.3.0 */ -public class BTableType extends BType implements TableType { +public class BTableType extends BType implements TableType, TypeWithShape { private final Type constraint; private Type keyType; @@ -162,4 +171,85 @@ public void setIntersectionType(IntersectionType intersectionType) { public boolean isAnydata() { return this.constraint.isAnydata(); } + + @Override + public SemType createSemType() { + return createSemTypeWithConstraint(tryInto(constraint)); + } + + private SemType createSemTypeWithConstraint(SemType constraintType) { + SemType semType; + Context cx = TypeChecker.context(); + if (fieldNames.length > 0) { + semType = TableUtils.tableContainingKeySpecifier(cx, constraintType, fieldNames); + } else if (keyType != null) { + semType = TableUtils.tableContainingKeyConstraint(cx, constraintType, tryInto(keyType)); + } else { + semType = TableUtils.tableContaining(cx.env, constraintType); + } + + if (isReadOnly()) { + semType = Core.intersect(semType, Builder.getReadonlyType()); + } + return semType; + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + BTable table = (BTable) object; + SemType cachedShape = table.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + SemType semtype = valueShape(cx, shapeSupplier, table); + return Optional.of(semtype); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return isReadOnly(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return Optional.of(valueShape(cx, shapeSupplierFn, (BTable) object)); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + 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) { + SemType constraintType = Builder.getNeverType(); + for (var value : table.values()) { + SemType valueShape = shapeSupplier.get(cx, value).orElse(SemType.tryInto(constraint)); + constraintType = Core.union(constraintType, valueShape); + } + return createSemTypeWithConstraint(constraintType); + } + + @Override + public boolean shouldCache() { + // TODO: remove this once we have fixed equals + return false; + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return constraint instanceof MayBeDependentType constraintType && constraintType.isDependentlyTyped(visited); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java index e4758fd5e8b8..ccda11d6f298 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTupleType.java @@ -24,6 +24,15 @@ import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.DefinitionContainer; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.values.AbstractArrayValue; import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TupleValueImpl; @@ -31,25 +40,36 @@ import java.util.List; import java.util.Objects; import java.util.Optional; +import java.util.Set; +import java.util.function.Function; import java.util.stream.Collectors; +import static io.ballerina.runtime.api.types.semtype.Builder.getNeverType; +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; + /** * {@code {@link BTupleType}} represents a tuple type in Ballerina. * * @since 0.995.0 */ -public class BTupleType extends BAnnotatableType implements TupleType { +public class BTupleType extends BAnnotatableType implements TupleType, TypeWithShape { private List tupleTypes; private Type restType; private int typeFlags; private final boolean readonly; + // This is used avoid unnecessary flag updates when we change the members. If this + // is set before accessing flags you must call {@code checkAllMembers}. + private volatile boolean flagsPoisoned = false; private IntersectionType immutableType; private IntersectionType intersectionType = null; public boolean isCyclic = false; private boolean resolving; private boolean resolvingReadonly; private String cachedToString; + private final DefinitionContainer defn = new DefinitionContainer<>(); + private final DefinitionContainer acceptedTypeDefn = new DefinitionContainer<>(); /** * Create a {@code BTupleType} which represents the tuple type. @@ -60,7 +80,7 @@ public BTupleType(List typeList) { super(null, null, Object.class); this.tupleTypes = typeList; this.restType = null; - checkAllMembers(); + this.flagsPoisoned = true; this.readonly = false; } @@ -154,6 +174,7 @@ public void setCyclic(boolean isCyclic) { } public void setMemberTypes(List members, Type restType) { + resetSemType(); if (members == null) { return; } @@ -166,7 +187,8 @@ public void setMemberTypes(List members, Type restType) { this.tupleTypes = members; this.restType = restType; } - checkAllMembers(); + flagsPoisoned = true; + defn.clear(); } @Override @@ -254,16 +276,24 @@ public boolean equals(Object o) { @Override public boolean isAnydata() { - return TypeFlags.isFlagOn(this.typeFlags, TypeFlags.ANYDATA); + return TypeFlags.isFlagOn(getTypeFlags(), TypeFlags.ANYDATA); } @Override public boolean isPureType() { - return TypeFlags.isFlagOn(this.typeFlags, TypeFlags.PURETYPE); + return TypeFlags.isFlagOn(getTypeFlags(), TypeFlags.PURETYPE); } @Override public int getTypeFlags() { + if (flagsPoisoned) { + synchronized (this) { + if (flagsPoisoned) { + checkAllMembers(); + flagsPoisoned = false; + } + } + } return this.typeFlags; } @@ -299,4 +329,108 @@ public void setIntersectionType(IntersectionType intersectionType) { public String getAnnotationKey() { return Utils.decodeIdentifier(this.typeName); } + + @Override + public SemType createSemType() { + Env env = Env.getInstance(); + if (defn.isDefinitionReady()) { + return defn.getSemType(env); + } + var result = defn.trySetDefinition(ListDefinition::new); + if (!result.updated()) { + return defn.getSemType(env); + } + ListDefinition ld = result.definition(); + return createSemTypeInner(env, ld, SemType::tryInto, mut()); + } + + private CellAtomicType.CellMutability mut() { + return isReadOnly() ? CELL_MUT_NONE : CellAtomicType.CellMutability.CELL_MUT_LIMITED; + } + + private SemType createSemTypeInner(Env env, ListDefinition ld, Function semTypeFunction, + CellAtomicType.CellMutability mut) { + SemType[] memberTypes = new SemType[tupleTypes.size()]; + for (int i = 0; i < tupleTypes.size(); i++) { + SemType memberType = semTypeFunction.apply(tupleTypes.get(i)); + if (Core.isNever(memberType)) { + return getNeverType(); + } + memberTypes[i] = memberType; + } + SemType rest = restType != null ? semTypeFunction.apply(restType) : getNeverType(); + return ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, rest, mut); + } + + @Override + public void resetSemType() { + defn.clear(); + super.resetSemType(); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return tupleTypes.stream().filter(each -> each instanceof MayBeDependentType) + .anyMatch(each -> ((MayBeDependentType) each).isDependentlyTyped(visited)); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + AbstractArrayValue value = (AbstractArrayValue) object; + SemType cachedShape = value.shapeOf(); + if (cachedShape != null) { + return Optional.of(cachedShape); + } + SemType semType = shapeOfInner(cx, shapeSupplier, value); + value.cacheShape(semType); + return Optional.of(semType); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return isReadOnly(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + return Optional.of(shapeOfInner(cx, shapeSupplier, (AbstractArrayValue) object)); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + Env env = cx.env; + if (acceptedTypeDefn.isDefinitionReady()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + var result = acceptedTypeDefn.trySetDefinition(ListDefinition::new); + if (!result.updated()) { + return Optional.of(acceptedTypeDefn.getSemType(env)); + } + ListDefinition ld = result.definition(); + return Optional.of(createSemTypeInner(env, ld, (type) -> ShapeAnalyzer.acceptedTypeOf(cx, type).orElseThrow(), + CELL_MUT_UNLIMITED)); + } + + private SemType shapeOfInner(Context cx, ShapeSupplier shapeSupplier, AbstractArrayValue value) { + Env env = cx.env; + ListDefinition defn = value.getReadonlyShapeDefinition(); + if (defn != null) { + return defn.getSemType(env); + } + int size = value.size(); + SemType[] memberTypes = new SemType[size]; + ListDefinition ld = new ListDefinition(); + value.setReadonlyShapeDefinition(ld); + for (int i = 0; i < size; i++) { + Optional memberType = shapeSupplier.get(cx, value.get(i)); + assert memberType.isPresent(); + memberTypes[i] = memberType.get(); + } + SemType semType = ld.defineListTypeWrapped(env, memberTypes, memberTypes.length, getNeverType(), mut()); + value.resetReadonlyShapeDefinition(); + return semType; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java index d6cd9c996ba3..1fdd10c91847 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BType.java @@ -22,10 +22,20 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.CacheableTypeDescriptor; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeCheckCache; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.MutableSemType; +import java.util.HashSet; import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; /** * {@code BType} represents a type in Ballerina. @@ -37,13 +47,18 @@ * * @since 0.995.0 */ -public abstract class BType implements Type { +public abstract non-sealed class BType extends SemType + implements Type, MutableSemType, Cloneable, CacheableTypeDescriptor, MayBeDependentType { + protected String typeName; protected Module pkg; protected Class valueClass; private int hashCode; private Type cachedReferredType = null; private Type cachedImpliedType = null; + private volatile SemType cachedSemType = null; + private volatile TypeCheckCache typeCheckCache; + private final ReadWriteLock typeCacheLock = new ReentrantReadWriteLock(); protected BType(String typeName, Module pkg, Class valueClass) { this.typeName = typeName; @@ -231,4 +246,99 @@ public void setCachedImpliedType(Type type) { public Type getCachedImpliedType() { return this.cachedImpliedType; } + + @Override + public SemType createSemType() { + throw new IllegalStateException("Child that are used for type checking must implement this method"); + } + + @Override + public void updateInnerSemTypeIfNeeded() { + synchronized (this) { + if (cachedSemType == null) { + cachedSemType = createSemType(); + setAll(cachedSemType.all()); + setSome(cachedSemType.some(), cachedSemType.subTypeData()); + } + } + } + + protected SemType getSemType() { + updateInnerSemTypeIfNeeded(); + return cachedSemType; + } + + @Override + public void resetSemType() { + cachedSemType = null; + } + + @Override + public BType clone() { + try { + BType clone = (BType) super.clone(); + clone.cachedSemType = null; + clone.setCachedImpliedType(null); + clone.setCachedReferredType(null); + return clone; + } catch (CloneNotSupportedException e) { + throw new AssertionError(); + } + } + + @Override + public boolean shouldCache() { + return this.pkg != null && this.typeName != null && !this.typeName.contains("$anon"); + } + + @Override + public final Optional cachedTypeCheckResult(Context cx, CacheableTypeDescriptor other) { + initializeCacheIfNeeded(cx); + typeCacheLock.readLock().lock(); + try { + return typeCheckCache.cachedTypeCheckResult(other); + } finally { + typeCacheLock.readLock().unlock(); + } + } + + private synchronized void initializeCacheIfNeeded(Context cx) { + typeCacheLock.readLock().lock(); + boolean shouldInitialize = typeCheckCache == null; + typeCacheLock.readLock().unlock(); + if (!shouldInitialize) { + return; + } + try { + typeCacheLock.writeLock().lock(); + if (typeCheckCache == null) { + typeCheckCache = cx.getTypeCheckCache(this); + } + } finally { + typeCacheLock.writeLock().unlock(); + } + } + + @Override + public final void cacheTypeCheckResult(CacheableTypeDescriptor other, boolean result) { + // This happening after checking the cache so it must be initialized by now + typeCheckCache.cacheTypeCheckResult(other, result); + } + + @Override + public final boolean isDependentlyTyped() { + return isDependentlyTyped(new HashSet<>()); + } + + @Override + public final boolean isDependentlyTyped(Set visited) { + if (!visited.add(this)) { + return false; + } + return isDependentlyTypedInner(visited); + } + + protected boolean isDependentlyTypedInner(Set visited) { + return false; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java index cc2e78d6a319..2e6384699111 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypeReferenceType.java @@ -25,16 +25,20 @@ import io.ballerina.runtime.api.types.IntersectionType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import java.util.Objects; import java.util.Optional; +import java.util.Set; /** * {@code TypeReferencedType} represents a type description which refers to another type. * * @since 2201.2.0 */ -public class BTypeReferenceType extends BAnnotatableType implements IntersectableReferenceType { +public class BTypeReferenceType extends BAnnotatableType implements IntersectableReferenceType, TypeWithShape { private final int typeFlags; private final boolean readOnly; @@ -126,4 +130,50 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public SemType createSemType() { + Type referredType = getReferredType(); + return tryInto(referredType); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return getReferredType() instanceof MayBeDependentType refType && refType.isDependentlyTyped(visited); + } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + if (!couldInherentTypeBeDifferent()) { + return Optional.of(getSemType()); + } + Type referredType = getReferredType(); + if (referredType instanceof TypeWithShape typeWithShape) { + return typeWithShape.inherentTypeOf(cx, shapeSupplier, object); + } + return Optional.empty(); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return referredType instanceof TypeWithShape typeWithShape && typeWithShape.couldInherentTypeBeDifferent(); + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + Type referredType = getReferredType(); + if (referredType instanceof TypeWithShape typeWithShape) { + return typeWithShape.shapeOf(cx, shapeSupplierFn, object); + } + return ShapeAnalyzer.shapeOf(cx, referredType); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + Type referredType = getReferredType(); + if (referredType instanceof TypeWithShape typeWithShape) { + return typeWithShape.acceptedTypeOf(cx); + } + return ShapeAnalyzer.acceptedTypeOf(cx, referredType); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java index 411d75cf9662..da696b1bf335 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BTypedescType.java @@ -24,19 +24,28 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.TypedescType; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.TypeChecker; +import io.ballerina.runtime.internal.types.semtype.TypedescUtils; import io.ballerina.runtime.internal.values.TypedescValue; import io.ballerina.runtime.internal.values.TypedescValueImpl; +import java.util.Set; + /** * {@code BTypedescType} represents a type of a type in the Ballerina type system. * * @since 0.995.0 */ public class BTypedescType extends BType implements TypedescType { - private Type constraint; + + private final Type constraint; public BTypedescType(String typeName, Module pkg) { super(typeName, pkg, Object.class); + constraint = null; } public BTypedescType(Type constraint) { @@ -84,4 +93,20 @@ public boolean isReadOnly() { public String toString() { return "typedesc" + "<" + constraint.toString() + ">"; } + + @Override + public SemType createSemType() { + if (constraint == null) { + return Builder.getTypeDescType(); + } + SemType constraint = tryInto(getConstraint()); + Context cx = TypeChecker.context(); + return TypedescUtils.typedescContaining(cx.env, constraint); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return constraint instanceof MayBeDependentType constraintType && + constraintType.isDependentlyTyped(visited); + } } 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 4fe1f164c653..bdd97fe67536 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 @@ -21,10 +21,16 @@ import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.flags.TypeFlags; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.SelectivelyImmutableReferenceType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; 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; @@ -44,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 = "|"; @@ -167,6 +173,7 @@ public void setMemberTypes(Type[] members) { } this.memberTypes = readonly ? getReadOnlyTypes(members) : Arrays.asList(members); setFlagsBasedOnMembers(); + resetSemType(); } public void setOriginalMemberTypes(Type[] originalMemberTypes) { @@ -182,6 +189,9 @@ private void setMemberTypes(List members) { } private void setMemberTypes(List members, List originalMembers) { + if (memberTypes != null) { + resetSemType(); + } if (members == null) { return; } @@ -193,7 +203,6 @@ private void setMemberTypes(List members, List originalMembers) { this.memberTypes = readonly ? getReadOnlyTypes(members, new HashSet<>(members.size())) : members; this.resolvingReadonly = false; setFlagsBasedOnMembers(); - setOriginalMemberTypes(originalMembers); } @@ -231,12 +240,14 @@ private boolean checkNillable(List memberTypes) { } private void addMember(Type type) { + resetSemType(); this.memberTypes.add(type); setFlagsBasedOnMembers(); this.originalMemberTypes.add(type); } public void addMembers(Type... types) { + resetSemType(); this.memberTypes.addAll(Arrays.asList(types)); setFlagsBasedOnMembers(); this.originalMemberTypes.addAll(Arrays.asList(types)); @@ -446,7 +457,7 @@ public void mergeUnionType(BUnionType unionType) { this.addMember(newArrayType); continue; } - } else if (member instanceof BMapType mapType) { + } else if (member instanceof MapType mapType) { if (mapType.getConstrainedType() == unionType) { BMapType newMapType = new BMapType(this, this.readonly); this.addMember(newMapType); @@ -457,7 +468,7 @@ public void mergeUnionType(BUnionType unionType) { BTableType newTableType = new BTableType(this, tableType.isReadOnly()); this.addMember(newTableType); continue; - } else if (tableType.getConstrainedType() instanceof BMapType mapType) { + } else if (tableType.getConstrainedType() instanceof MapType mapType) { if (mapType.getConstrainedType() == unionType) { BMapType newMapType = new BMapType(this); BTableType newTableType = new BTableType(newMapType, @@ -540,4 +551,22 @@ public Optional getIntersectionType() { public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public SemType createSemType() { + return memberTypes.stream().map(SemType::tryInto).reduce(Builder.getNeverType(), Core::union); + } + + @Override + protected boolean isDependentlyTypedInner(Set visited) { + return memberTypes.stream() + .filter(each -> each instanceof MayBeDependentType) + .anyMatch(type -> ((MayBeDependentType) type).isDependentlyTyped(visited)); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + return Optional.of(memberTypes.stream().map(each -> ShapeAnalyzer.acceptedTypeOf(cx, each).orElseThrow()) + .reduce(Builder.getNeverType(), Core::union)); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java index 05e1cde6985c..45dd60c0ad10 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/BXmlType.java @@ -20,11 +20,21 @@ import io.ballerina.runtime.api.Module; import io.ballerina.runtime.api.constants.TypeConstants; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.ParameterizedType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.XmlType; +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.internal.types.semtype.XmlUtils; import io.ballerina.runtime.internal.values.ReadOnlyUtils; +import io.ballerina.runtime.internal.values.XmlComment; +import io.ballerina.runtime.internal.values.XmlItem; +import io.ballerina.runtime.internal.values.XmlPi; import io.ballerina.runtime.internal.values.XmlSequence; +import io.ballerina.runtime.internal.values.XmlText; import io.ballerina.runtime.internal.values.XmlValue; import java.util.Optional; @@ -35,10 +45,10 @@ * @since 0.995.0 */ @SuppressWarnings("unchecked") -public class BXmlType extends BType implements XmlType { +public class BXmlType extends BType implements XmlType, TypeWithShape { private final int tag; - public Type constraint; + public final Type constraint; private final boolean readonly; private IntersectionType immutableType; private IntersectionType intersectionType = null; @@ -63,6 +73,13 @@ public BXmlType(String typeName, Module pkg, int tag, boolean readonly) { this.constraint = null; } + public BXmlType(String typeName, Type constraint, Module pkg, int tag, boolean readonly) { + super(typeName, pkg, XmlValue.class); + this.tag = tag; + this.readonly = readonly; + this.constraint = constraint; + } + public BXmlType(String typeName, Type constraint, Module pkg, boolean readonly) { super(typeName, pkg, XmlValue.class); this.tag = TypeTags.XML_TAG; @@ -138,8 +155,103 @@ public Optional getIntersectionType() { return this.intersectionType == null ? Optional.empty() : Optional.of(this.intersectionType); } + @Override + public SemType createSemType() { + SemType semType; + if (constraint == null) { + semType = pickTopType(); + } else { + SemType contraintSemtype; + if (constraint instanceof ParameterizedType parameterizedType) { + contraintSemtype = tryInto(parameterizedType.getParamValueType()); + } else { + contraintSemtype = tryInto(constraint); + } + semType = XmlUtils.xmlSequence(contraintSemtype); + } + return isReadOnly() ? Core.intersect(Builder.getReadonlyType(), semType) : semType; + } + + private SemType pickTopType() { + return switch (tag) { + case TypeTags.XML_TAG -> Builder.getXmlType(); + case TypeTags.XML_ELEMENT_TAG -> Builder.getXmlElementType(); + case TypeTags.XML_COMMENT_TAG -> Builder.getXmlCommentType(); + case TypeTags.XML_PI_TAG -> Builder.getXmlPIType(); + case TypeTags.XML_TEXT_TAG -> Builder.getXmlTextType(); + default -> throw new IllegalStateException("Unexpected value: " + tag); + }; + } + @Override public void setIntersectionType(IntersectionType intersectionType) { this.intersectionType = intersectionType; } + + @Override + public Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplier, Object object) { + XmlValue xmlValue = (XmlValue) object; + if (!isReadOnly(xmlValue)) { + return Optional.of(getSemType()); + } + return readonlyShapeOf(object); + } + + @Override + public boolean couldInherentTypeBeDifferent() { + return true; + } + + @Override + public Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object) { + return readonlyShapeOf(object).map(semType -> Core.intersect(semType, Builder.getReadonlyType())); + } + + @Override + public Optional acceptedTypeOf(Context cx) { + return Optional.of(getSemType()); + } + + private Optional readonlyShapeOf(Object object) { + if (object instanceof XmlSequence xmlSequence) { + // We represent xml as an empty sequence + var children = xmlSequence.getChildrenList(); + if (children.isEmpty()) { + return Optional.of(XmlUtils.xmlSingleton(XmlUtils.XML_PRIMITIVE_NEVER)); + } else if (children.size() == 1) { + // Not entirely sure if this is correct, but needed for passing tests + return readonlyShapeOf(children.get(0)); + } + return children.stream() + .map(this::readonlyShapeOf) + .filter(Optional::isPresent) + .map(Optional::get) + .reduce(Core::union) + .map(XmlUtils::xmlSequence); + } else if (object instanceof XmlText) { + // Text is inherently readonly + return Optional.of(Builder.getXmlTextType()); + } else if (object instanceof XmlItem xml) { + return getSemType(xml, Builder.getXmlElementType()); + } else if (object instanceof XmlComment xml) { + return getSemType(xml, Builder.getXmlCommentType()); + } else if (object instanceof XmlPi xml) { + return getSemType(xml, Builder.getXmlPIType()); + } + throw new IllegalArgumentException("Unexpected xml value: " + object); + } + + private static Optional getSemType(XmlValue xml, SemType baseType) { + if (isReadOnly(xml)) { + return Optional.of(Core.intersect(baseType, Builder.getReadonlyType())); + } + return Optional.of(baseType); + } + + private static boolean isReadOnly(XmlValue xmlValue) { + if (xmlValue instanceof XmlSequence || xmlValue instanceof XmlText) { + return true; + } + return xmlValue.getType().isReadOnly(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/DistinctIdSupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/DistinctIdSupplier.java new file mode 100644 index 000000000000..771ea177cfa5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/DistinctIdSupplier.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types; + +import io.ballerina.runtime.api.types.TypeId; +import io.ballerina.runtime.api.types.TypeIdSet; +import io.ballerina.runtime.api.types.semtype.Env; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + +/** + * A supplier that provides a list of distinct ids for a given type id set. + * + * @since 2201.11.0 + */ +final class DistinctIdSupplier implements Supplier> { + + private List ids = null; + private static final Map allocatedIds = new ConcurrentHashMap<>(); + private final Env env; + private final TypeIdSet typeIdSet; + + DistinctIdSupplier(Env env, TypeIdSet typeIdSet) { + this.env = env; + this.typeIdSet = typeIdSet; + } + + public synchronized Collection get() { + if (ids != null) { + return ids; + } + if (typeIdSet == null) { + return List.of(); + } + ids = typeIdSet.getIds().stream().map(TypeIdWrapper::new).map(typeId -> allocatedIds.computeIfAbsent(typeId, + ignored -> env.distinctAtomCountGetAndIncrement())) + .toList(); + return ids; + } + + // This is to avoid whether id is primary or not affecting the hashcode. + private record TypeIdWrapper(TypeId typeId) { + + @Override + public boolean equals(Object obj) { + if (obj instanceof TypeIdWrapper other) { + return typeId.getName().equals(other.typeId().getName()) && + typeId.getPkg().equals(other.typeId().getPkg()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(typeId.getPkg(), typeId.getName()); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/BddNode.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/MayBeDependentType.java similarity index 58% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/BddNode.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/MayBeDependentType.java index 9c657c468581..2a68601ce4bc 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/BddNode.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/MayBeDependentType.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -14,20 +14,21 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * */ -package io.ballerina.semtype.subtypedata; -import io.ballerina.semtype.Atom; -import io.ballerina.semtype.Bdd; +package io.ballerina.runtime.internal.types; + +import java.util.Set; /** - * Bdd node. + * Represents a type that may be dependently typed. * - * @since 2.0.0 + * @since 2201.11.0 */ -public class BddNode implements Bdd { - Atom atom; - BddNode left; - BddNode middle; - BddNode right; +public interface MayBeDependentType { + + boolean isDependentlyTyped(); + + boolean isDependentlyTyped(Set visited); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/ShapeSupplier.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/ShapeSupplier.java new file mode 100644 index 000000000000..999986483930 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/ShapeSupplier.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +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; + +/** + * Function that can be used to get the shape of a value. + * + * @since 2201.11.0 + */ +@FunctionalInterface +public interface ShapeSupplier { + + Optional get(Context cx, Object object); +} 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..6dda26b4a8d6 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithAcceptedType.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +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. + * + * @since 2201.11.0 + */ +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 new file mode 100644 index 000000000000..259062fc6896 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/TypeWithShape.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +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; + +/** + * Types that are not basic types and have values whose shape could be different form the actual type (i.e. not handles) + * must implement this interface. Note that multiple values could share the same instance of TypeWithShape. Ideally + * 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}. + * + * @since 2201.11.0 + */ +public interface TypeWithShape extends TypeWithAcceptedType { + + Optional inherentTypeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); + + Optional shapeOf(Context cx, ShapeSupplier shapeSupplierFn, Object object); + + boolean couldInherentTypeBeDifferent(); +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/UniformSubtype.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java similarity index 52% rename from semtypes/src/main/java/io/ballerina/semtype/UniformSubtype.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java index bcd02fa91e34..104ed50d84ba 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/UniformSubtype.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/AllOrNothing.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -14,20 +14,20 @@ * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. + * */ -package io.ballerina.semtype; + +package io.ballerina.runtime.internal.types.semtype; /** - * UniformSubtype node. + * Represent cases where a subtype is either all or nothing of the basic type. + * For example if StringSubType has All as it's subtype data that means subtype + * is actually String basic type and nothing means it doesn't have any string + * subtype * - * @since 2.0.0 + * @since 2201.11.0 */ -public class UniformSubtype { - public final int uniformTypeCode; - public final SubtypeData subtypeData; - - public UniformSubtype(int uniformTypeCode, SubtypeData subtypeData) { - this.uniformTypeCode = uniformTypeCode; - this.subtypeData = subtypeData; - } +public enum AllOrNothing implements SubTypeData { + ALL, + NOTHING } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBooleanSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBooleanSubType.java new file mode 100644 index 000000000000..a93adceb286b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BBooleanSubType.java @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +/** + * Runtime representation of Boolean Sub Type. + * + * @since 2201.11.0 + */ +public final class BBooleanSubType extends SubType { + + private final BBooleanSubTypeData data; + private static final BBooleanSubType ALL = new BBooleanSubType(BBooleanSubTypeData.ALL); + private static final BBooleanSubType NOTHING = new BBooleanSubType(BBooleanSubTypeData.NOTHING); + private static final BBooleanSubType TRUE = new BBooleanSubType(BBooleanSubTypeData.TRUE); + private static final BBooleanSubType FALSE = new BBooleanSubType(BBooleanSubTypeData.FALSE); + + private BBooleanSubType(BBooleanSubTypeData data) { + super(data.isAll(), data.isNothing()); + this.data = data; + } + + public static BBooleanSubType from(boolean value) { + return value ? TRUE : FALSE; + } + + @Override + public SubType union(SubType otherSubtype) { + if (!(otherSubtype instanceof BBooleanSubType other)) { + throw new IllegalArgumentException("union of different subtypes"); + } + if (this.isAll() || other.isAll()) { + return ALL; + } + if (this.isNothing()) { + return other; + } + if (other.isNothing()) { + return this; + } + if (this.data.value == other.data.value) { + return this; + } + return ALL; + } + + @Override + public SubType intersect(SubType otherSubtype) { + if (!(otherSubtype instanceof BBooleanSubType other)) { + throw new IllegalArgumentException("intersection of different subtypes"); + } + if (this.isAll()) { + return other; + } + if (other.isAll()) { + return this; + } + if (this.isNothing() || other.isNothing()) { + return NOTHING; + } + if (this.data.value == other.data.value) { + return this; + } + return NOTHING; + } + + @Override + public SubType diff(SubType otherSubtype) { + if (!(otherSubtype instanceof BBooleanSubType other)) { + throw new IllegalArgumentException("diff of different subtypes"); + } + if (this.isAll() && other.isAll()) { + return NOTHING; + } + if (this.isNothing() || other.isAll()) { + return NOTHING; + } + if (other.isNothing()) { + return this; + } + if (this.isAll()) { + return from(!other.data.value); + } + return this.data.value == other.data.value ? NOTHING : this; + } + + @Override + public SubType complement() { + if (isAll()) { + return NOTHING; + } + if (isNothing()) { + return ALL; + } + return from(!data.value); + } + + @Override + public boolean isEmpty(Context cx) { + return data.isNothing(); + } + + @Override + public SubTypeData data() { + return data.toData(); + } + + // This is instance controlled so only 4 possible instances exists. Default equals is therefore correct + @Override + public int hashCode() { + if (this == ALL) { + return 0; + } + if (this == NOTHING) { + return 1; + } + if (this == TRUE) { + return 2; + } + if (this == FALSE) { + return 3; + } + assert false : "unexpected BBooleanSubType instance"; + return -1; + } + + private record BBooleanSubTypeData(boolean isAll, boolean isNothing, boolean value) { + + private static final BBooleanSubTypeData ALL = new BBooleanSubTypeData(true, false, false); + private static final BBooleanSubTypeData NOTHING = new BBooleanSubTypeData(false, true, false); + private static final BBooleanSubTypeData TRUE = new BBooleanSubTypeData(false, false, true); + private static final BBooleanSubTypeData FALSE = new BBooleanSubTypeData(false, false, false); + + SubTypeData toData() { + if (isAll()) { + return AllOrNothing.ALL; + } else if (isNothing()) { + return AllOrNothing.NOTHING; + } + return new BooleanSubTypeData(value()); + } + } + + private record BooleanSubTypeData(boolean value) implements SubTypeData { + + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubType.java new file mode 100644 index 000000000000..2192eec3e530 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubType.java @@ -0,0 +1,52 @@ +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; +import io.ballerina.runtime.api.types.semtype.TypeAtom; + +/** + * Represents a subtype of a Cell. + * + * @since 2201.11.0 + */ +public abstract sealed class BCellSubType extends SubType implements DelegatedSubType + permits BCellSubTypeImpl, BCellSubTypeSimple { + + public BCellSubType(boolean all, boolean nothing) { + super(all, nothing); + } + + public static BCellSubType createDelegate(SubType inner) { + Bdd bdd; + if (inner instanceof Bdd b) { + bdd = b; + } else if (inner instanceof BCellSubTypeImpl bCellImpl) { + bdd = bCellImpl.inner(); + } else if (inner instanceof BCellSubTypeSimple simple) { + return simple; + } else { + throw new IllegalArgumentException("Unexpected inner type"); + } + if (!(bdd instanceof BddNode bddNode && bddNode.isSimple())) { + return new BCellSubTypeImpl(bdd); + } + Atom atom = bddNode.atom(); + if (!(atom instanceof TypeAtom typeAtom)) { + return new BCellSubTypeImpl(bdd); + } + CellAtomicType atomicType = (CellAtomicType) typeAtom.atomicType(); + SemType ty = atomicType.ty(); + // We have special logic when it comes to handling undef that needs to be updated to deal with simple cell + // TODO: probably we can also handle immutable cells as well + if (Core.containsBasicType(ty, Builder.getUndefType()) || ty.some() != 0 || + atomicType.mut() != CellAtomicType.CellMutability.CELL_MUT_LIMITED) { + return new BCellSubTypeImpl(bdd); + } + return new BCellSubTypeSimple(ty, bddNode); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeImpl.java new file mode 100644 index 000000000000..0335d6513051 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeImpl.java @@ -0,0 +1,186 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Conjunction; +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.SubType; + +import java.util.Objects; +import java.util.function.Predicate; + +/** + * Runtime representation of CellSubType. + * + * @since 2201.11.0 + */ +final class BCellSubTypeImpl extends BCellSubType implements DelegatedSubType { + + private final Bdd inner; + + BCellSubTypeImpl(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + @Override + public SubType union(SubType other) { + if (other instanceof BCellSubType otherCell) { + return createDelegate(inner.union(otherCell.inner())); + } + throw new IllegalArgumentException("union of different subtypes"); + } + + @Override + public SubType intersect(SubType other) { + if (other instanceof BCellSubType otherCell) { + return createDelegate(inner.intersect(otherCell.inner())); + } + throw new IllegalArgumentException("intersect of different subtypes"); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return Bdd.bddEvery(cx, inner, BCellSubTypeImpl::cellFormulaIsEmpty); + } + + @Override + public SubType diff(SubType other) { + if (other instanceof BCellSubType otherCell) { + return createDelegate(inner.diff(otherCell.inner())); + } + throw new IllegalArgumentException("diff of different subtypes"); + + } + + @Override + public SubTypeData data() { + throw new IllegalStateException("unimplemented"); + } + + private static boolean cellFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { + CellAtomicType combined; + if (posList == null) { + combined = CellAtomicType.from(Builder.getValType(), CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + } else { + combined = CellAtomicType.cellAtomType(posList.atom()); + Conjunction p = posList.next(); + while (p != null) { + combined = CellAtomicType.intersectCellAtomicType(combined, CellAtomicType.cellAtomType(p.atom())); + p = p.next(); + } + } + return !cellInhabited(cx, combined, negList); + } + + private static boolean cellInhabited(Context cx, CellAtomicType posCell, Conjunction negList) { + SemType pos = posCell.ty(); + if (Core.isEmpty(cx, pos)) { + return false; + } + return switch (posCell.mut()) { + case CELL_MUT_NONE -> cellMutNoneInhabited(cx, pos, negList); + case CELL_MUT_LIMITED -> cellMutLimitedInhabited(cx, pos, negList); + default -> cellMutUnlimitedInhabited(cx, pos, negList); + }; + } + + private static boolean cellMutUnlimitedInhabited(Context cx, SemType pos, Conjunction negList) { + Conjunction neg = negList; + while (neg != null) { + if (CellAtomicType.cellAtomType(neg.atom()).mut() == CellAtomicType.CellMutability.CELL_MUT_LIMITED && + Core.isSameType(cx, Builder.getValType(), CellAtomicType.cellAtomType(neg.atom()).ty())) { + return false; + } + neg = neg.next(); + } + SemType negListUnionResult = filteredCellListUnion(negList, + conjunction -> CellAtomicType.cellAtomType(conjunction.atom()).mut() == + CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + // We expect `isNever` condition to be `true` when there are no negative atoms with unlimited mutability. + // Otherwise, we do `isEmpty` to conclude on the inhabitance. + return Core.isNever(negListUnionResult) || !Core.isEmpty(cx, Core.diff(pos, negListUnionResult)); + } + + private static boolean cellMutLimitedInhabited(Context cx, SemType pos, Conjunction negList) { + if (negList == null) { + return true; + } + CellAtomicType negAtomicCell = CellAtomicType.cellAtomType(negList.atom()); + if ((negAtomicCell.mut().compareTo(CellAtomicType.CellMutability.CELL_MUT_LIMITED) >= 0) && + Core.isEmpty(cx, Core.diff(pos, negAtomicCell.ty()))) { + return false; + } + return cellMutLimitedInhabited(cx, pos, negList.next()); + } + + private static boolean cellMutNoneInhabited(Context cx, SemType pos, Conjunction negList) { + SemType negListUnionResult = cellListUnion(negList); + // We expect `isNever` condition to be `true` when there are no negative atoms. + // Otherwise, we do `isEmpty` to conclude on the inhabitance. + return Core.isNever(negListUnionResult) || !Core.isEmpty(cx, Core.diff(pos, negListUnionResult)); + } + + private static SemType cellListUnion(Conjunction negList) { + return filteredCellListUnion(negList, neg -> true); + } + + private static SemType filteredCellListUnion(Conjunction negList, Predicate predicate) { + SemType negUnion = Builder.getNeverType(); + Conjunction neg = negList; + while (neg != null) { + if (predicate.test(neg)) { + negUnion = Core.union(negUnion, CellAtomicType.cellAtomType(neg.atom()).ty()); + } + neg = neg.next(); + } + return negUnion; + } + + @Override + public Bdd inner() { + return inner; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BCellSubTypeImpl other)) { + return false; + } + return Objects.equals(inner, other.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeSimple.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeSimple.java new file mode 100644 index 000000000000..8166bf862624 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BCellSubTypeSimple.java @@ -0,0 +1,137 @@ +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddAllOrNothing; +import io.ballerina.runtime.api.types.semtype.BddNode; +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.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; +import io.ballerina.runtime.api.types.semtype.TypeAtom; +import io.ballerina.runtime.internal.TypeChecker; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Stream; + +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; + +/** + * Simplified representation of cell if the type is only basic type union and mutability is limited. + * + * @since 2201.11.0 + */ +final class BCellSubTypeSimple extends BCellSubType implements DelegatedSubType { + + private final List pos; + private final List neg; + private BddNode inner; + + BCellSubTypeSimple(SemType type) { + super(type.all() == BasicTypeCode.VT_MASK, type.all() == 0); + assert type.some() == 0; + this.pos = List.of(type); + this.neg = List.of(); + } + + BCellSubTypeSimple(SemType type, BddNode bddNode) { + this(type); + inner = bddNode; + } + + private BCellSubTypeSimple(List pos, List neg) { + super(false, false); + this.pos = pos; + this.neg = neg; + } + + @Override + public SubType union(SubType other) { + if (other instanceof BCellSubTypeSimple simple) { + // P1\N1 U P2\N2 = (P1 U P2)\(N1 U N2) + List combinedPos = Stream.concat(pos.stream(), simple.pos.stream()).toList(); + List combinedNeg = Stream.concat(neg.stream(), simple.neg.stream()).toList(); + return new BCellSubTypeSimple(combinedPos, combinedNeg); + } else if (other instanceof BCellSubTypeImpl complex) { + return createDelegate(inner().union(complex.inner())); + } + throw new IllegalArgumentException("union of different subtypes"); + } + + @Override + public SubType intersect(SubType other) { + if (other instanceof BCellSubTypeSimple simple) { + // P1\N1 ∩ P2\N2 = (P1 ∩ P2)\(N1 U N2) + SemType pos = + Stream.concat(this.pos.stream(), simple.pos.stream()).reduce(Builder.getValType(), Core::intersect); + List neg = Stream.concat(this.neg.stream(), simple.neg.stream()).toList(); + return new BCellSubTypeSimple(List.of(pos), neg); + } else if (other instanceof BCellSubTypeImpl complex) { + return createDelegate(inner().intersect(complex.inner())); + } + throw new IllegalArgumentException("intersection of different subtypes"); + } + + @Override + public SubType complement() { + return new BCellSubTypeSimple(neg, pos); + } + + @Override + public boolean isEmpty(Context cx) { + if (pos.isEmpty()) { + return true; + } + SemType posUnion = pos.stream().reduce(Builder.getNeverType(), Core::union); + if (neg.isEmpty()) { + return Core.isEmpty(cx, posUnion); + } + return neg.stream().anyMatch(neg -> Core.isEmpty(cx, Core.diff(posUnion, neg))); + } + + @Override + public SubTypeData data() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public SubType inner() { + if (inner != null) { + return inner; + } + Env env = TypeChecker.getEnv(); + Optional posBdd = + pos.stream().map(semType -> fromSemType(env, semType)).reduce((acum, bdd) -> (Bdd) acum.union(bdd)); + if (posBdd.isEmpty()) { + return BddAllOrNothing.NOTHING; + } + Optional negBdd = + neg.stream().map(semType -> fromSemType(env, semType)).reduce((acum, bdd) -> (Bdd) acum.union(bdd)); + if (negBdd.isEmpty()) { + return posBdd.get(); + } + return posBdd.get().diff(negBdd.get()); + } + + private static Bdd fromSemType(Env env, SemType type) { + CellAtomicType atomicCell = CellAtomicType.from(type, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + TypeAtom atom = env.cellAtom(atomicCell); + return bddAtom(atom); + } + + @Override + public int hashCode() { + return Stream.concat(pos.stream(), neg.stream()).map(SemType::hashCode).reduce(0, Integer::sum); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BCellSubTypeSimple other)) { + return false; + } + return pos.equals(other.pos) && neg.equals(other.neg); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BDecimalSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BDecimalSubType.java new file mode 100644 index 000000000000..03d2902e17db --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BDecimalSubType.java @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Runtime representation of DecimalSubType. + * + * @since 2201.11.0 + */ +public final class BDecimalSubType extends SubType { + + final SubTypeData data; + + private BDecimalSubType(SubTypeData data) { + super(data == AllOrNothing.ALL, data == AllOrNothing.NOTHING); + this.data = data; + } + + private static final BDecimalSubType ALL = new BDecimalSubType(AllOrNothing.ALL); + private static final BDecimalSubType NOTHING = new BDecimalSubType(AllOrNothing.NOTHING); + + public static BDecimalSubType createDecimalSubType(boolean allowed, BigDecimal[] values) { + if (values.length == 0) { + if (!allowed) { + return ALL; + } else { + return NOTHING; + } + } + Arrays.sort(values); + return new BDecimalSubType(new DecimalSubTypeData(allowed, values)); + } + + @Override + public SubType union(SubType otherSubtype) { + BDecimalSubType other = (BDecimalSubType) otherSubtype; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return this; + } else { + return other; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return other; + } else { + return this; + } + } + List values = new ArrayList<>(); + DecimalSubTypeData data = (DecimalSubTypeData) this.data; + DecimalSubTypeData otherData = (DecimalSubTypeData) other.data; + boolean allowed = data.union(otherData, values); + return createDecimalSubType(allowed, values.toArray(BigDecimal[]::new)); + } + + @Override + public SubType intersect(SubType otherSubtype) { + BDecimalSubType other = (BDecimalSubType) otherSubtype; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return other; + } else { + return NOTHING; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return this; + } else { + return NOTHING; + } + } + List values = new ArrayList<>(); + DecimalSubTypeData data = (DecimalSubTypeData) this.data; + DecimalSubTypeData otherData = (DecimalSubTypeData) other.data; + boolean allowed = data.intersect(otherData, values); + return createDecimalSubType(allowed, values.toArray(BigDecimal[]::new)); + } + + @Override + public SubType complement() { + if (data == AllOrNothing.ALL) { + return NOTHING; + } else if (data == AllOrNothing.NOTHING) { + return ALL; + } + DecimalSubTypeData data = (DecimalSubTypeData) this.data; + return createDecimalSubType(!data.allowed, data.values); + } + + @Override + public boolean isEmpty(Context cx) { + return data == AllOrNothing.NOTHING; + } + + @Override + public SubTypeData data() { + return data; + } + + public BigDecimal defaultValue() { + if (data instanceof DecimalSubTypeData subTypeData && subTypeData.allowed && subTypeData.values.length == 1) { + return subTypeData.values[0]; + } + return null; + } + + @Override + public String toString() { + if (data instanceof DecimalSubTypeData subTypeData && subTypeData.allowed) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < subTypeData.values.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(subTypeData.values[i]); + } + return sb.toString(); + } + return "decimal"; + } + + private static final class DecimalSubTypeData extends EnumerableSubtypeData implements SubTypeData { + + private final boolean allowed; + private final BigDecimal[] values; + + private DecimalSubTypeData(boolean allowed, BigDecimal[] values) { + this.allowed = allowed; + this.values = values; + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public BigDecimal[] values() { + return values; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BErrorSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BErrorSubType.java new file mode 100644 index 000000000000..5045122f9a2a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BErrorSubType.java @@ -0,0 +1,117 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEveryPositive; + +/** + * Runtime representation of a subtype of error type. + * + * @since 2201.11.0 + */ +public class BErrorSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BErrorSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BErrorSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BErrorSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BErrorSubType bError) { + return new BErrorSubType(bError.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BErrorSubType otherError)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherError.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BErrorSubType otherError)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherError.inner)); + } + + @Override + public SubType complement() { + return createDelegate(Builder.getBddSubtypeRo().diff(inner)); + } + + @Override + public boolean isEmpty(Context cx) { + Bdd b = inner; + // The goal of this is to ensure that mappingFormulaIsEmpty call in errorBddIsEmpty beneath + // does not get an empty posList, because it will interpret that + // as `map` rather than `readonly & map`. + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getBddSubtypeRo()) : b; + return cx.memoSubtypeIsEmpty(cx.mappingMemo, BErrorSubType::errorBddIsEmpty, b); + } + + private static boolean errorBddIsEmpty(Context cx, Bdd b) { + return bddEveryPositive(cx, b, null, null, BMappingSubType::mappingFormulaIsEmpty); + } + + @Override + public SubTypeData data() { + return inner(); + } + + @Override + public Bdd inner() { + return inner; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BErrorSubType that)) { + return false; + } + return Objects.equals(inner, that.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFloatSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFloatSubType.java new file mode 100644 index 000000000000..333e986ae9be --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFloatSubType.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +/** + * Runtime representation of FloatSubType. + * + * @since 2201.11.0 + */ +public final class BFloatSubType extends SubType { + + final SubTypeData data; + + private BFloatSubType(SubTypeData data) { + super(data == AllOrNothing.ALL, data == AllOrNothing.NOTHING); + this.data = data; + } + + private static final BFloatSubType ALL = new BFloatSubType(AllOrNothing.ALL); + private static final BFloatSubType NOTHING = new BFloatSubType(AllOrNothing.NOTHING); + + public static BFloatSubType createFloatSubType(boolean allowed, Double[] values) { + if (values.length == 0) { + if (!allowed) { + return ALL; + } else { + return NOTHING; + } + } + return new BFloatSubType(new FloatSubTypeData(allowed, values)); + } + + @Override + public SubType union(SubType otherSubtype) { + BFloatSubType other = (BFloatSubType) otherSubtype; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return this; + } else { + return other; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return other; + } else { + return this; + } + } + List values = new ArrayList<>(); + FloatSubTypeData data = (FloatSubTypeData) this.data; + FloatSubTypeData otherData = (FloatSubTypeData) other.data; + boolean allowed = data.union(otherData, values); + return createFloatSubType(allowed, values.toArray(Double[]::new)); + } + + @Override + public SubType intersect(SubType otherSubtype) { + BFloatSubType other = (BFloatSubType) otherSubtype; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return other; + } else { + return NOTHING; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return this; + } else { + return NOTHING; + } + } + List values = new ArrayList<>(); + FloatSubTypeData data = (FloatSubTypeData) this.data; + FloatSubTypeData otherData = (FloatSubTypeData) other.data; + boolean allowed = data.intersect(otherData, values); + return createFloatSubType(allowed, values.toArray(Double[]::new)); + } + + @Override + public SubType complement() { + if (data == AllOrNothing.ALL) { + return NOTHING; + } else if (data == AllOrNothing.NOTHING) { + return ALL; + } + FloatSubTypeData data = (FloatSubTypeData) this.data; + return createFloatSubType(!data.allowed, data.values); + } + + @Override + public boolean isEmpty(Context cx) { + return data == AllOrNothing.NOTHING; + } + + @Override + public SubTypeData data() { + return data; + } + + static final class FloatSubTypeData extends EnumerableSubtypeData implements SubTypeData { + + private final boolean allowed; + private final Double[] values; + + private FloatSubTypeData(boolean allowed, Double[] values) { + this.allowed = allowed; + this.values = filteredValues(values); + } + + private static Double[] filteredValues(Double[] values) { + for (int i = 0; i < values.length; i++) { + values[i] = canon(values[i]); + } + if (values.length < 2) { + return values; + } + Arrays.sort(values); + Double[] buffer = new Double[values.length]; + buffer[0] = values[0]; + int bufferLen = 1; + for (int i = 1; i < values.length; i++) { + Double value = values[i]; + Double prevValue = values[i - 1]; + if (isSame(value, prevValue)) { + continue; + } + buffer[bufferLen++] = value; + } + return Arrays.copyOf(buffer, bufferLen); + } + + private static Double canon(Double d) { + if (d.equals(0.0) || d.equals(-0.0)) { + return 0.0; + } + return d; + } + + private static boolean isSame(double f1, double f2) { + if (Double.isNaN(f1)) { + return Double.isNaN(f2); + } + return f1 == f2; + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public Double[] values() { + return values; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFunctionSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFunctionSubType.java new file mode 100644 index 000000000000..cad6312b7744 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFunctionSubType.java @@ -0,0 +1,172 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Conjunction; +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.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; + +/** + * Runtime representation of a subtype of function type. + * + * @since 2201.11.0 + */ +public class BFunctionSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BFunctionSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BFunctionSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BFunctionSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BFunctionSubType bFunction) { + return new BFunctionSubType(bFunction.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BFunctionSubType otherFn)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherFn.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BFunctionSubType otherList)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherList.inner)); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return cx.memoSubtypeIsEmpty(cx.functionMemo, + (context, bdd) -> bddEvery(context, bdd, BFunctionSubType::functionFormulaIsEmpty), inner); + } + + private static boolean functionFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + return functionPathIsEmpty(cx, functionUnionParams(cx, pos), functionUnionQualifiers(cx, pos), pos, neg); + } + + private static boolean functionPathIsEmpty(Context cx, SemType params, SemType qualifier, Conjunction pos, + Conjunction neg) { + if (neg == null) { + return false; + } + FunctionAtomicType t = cx.functionAtomicType(neg.atom()); + SemType t0 = t.paramType(); + SemType t1 = t.retType(); + SemType t2 = t.qualifiers(); + return (Core.isSubType(cx, qualifier, t2) && Core.isSubType(cx, t0, params) && + functionPhi(cx, t0, Core.complement(t1), pos)) + || functionPathIsEmpty(cx, params, qualifier, pos, neg.next()); + } + + private static boolean functionPhi(Context cx, SemType t0, SemType t1, Conjunction pos) { + if (pos == null) { + // t0 is NEVER only for function top types with qualifiers + return !Core.isNever(t0) && (Core.isEmpty(cx, t0) || Core.isEmpty(cx, t1)); + } + return functionPhiInner(cx, t0, t1, pos); + } + + private static boolean functionPhiInner(Context cx, SemType t0, SemType t1, Conjunction pos) { + if (pos == null) { + return Core.isEmpty(cx, t0) || Core.isEmpty(cx, t1); + } else { + FunctionAtomicType s = cx.functionAtomicType(pos.atom()); + SemType s0 = s.paramType(); + SemType s1 = s.retType(); + return (Core.isSubType(cx, t0, s0) + || Core.isSubType(cx, functionIntersectRet(cx, pos.next()), Core.complement(t1))) + && functionPhiInner(cx, t0, Core.intersect(t1, s1), pos.next()) + && functionPhiInner(cx, Core.diff(t0, s0), t1, pos.next()); + } + } + + private static SemType functionIntersectRet(Context cx, Conjunction pos) { + if (pos == null) { + return Builder.getValType(); + } + return Core.intersect(cx.functionAtomicType(pos.atom()).retType(), functionIntersectRet(cx, pos.next())); + } + + private static SemType functionUnionParams(Context cx, Conjunction pos) { + if (pos == null) { + return Builder.getNeverType(); + } + return Core.union(cx.functionAtomicType(pos.atom()).paramType(), functionUnionParams(cx, pos.next())); + } + + private static SemType functionUnionQualifiers(Context cx, Conjunction pos) { + if (pos == null) { + return Builder.getNeverType(); + } + return Core.union(cx.functionAtomicType(pos.atom()).qualifiers(), functionUnionQualifiers(cx, pos.next())); + } + + @Override + public SubTypeData data() { + throw new IllegalStateException("unimplemented"); + } + + @Override + public Bdd inner() { + return inner; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BMappingSubType that)) { + return false; + } + return Objects.equals(inner, that.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFutureSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFutureSubType.java new file mode 100644 index 000000000000..c9389cff9671 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BFutureSubType.java @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; + +/** + * Runtime representation of a subtype of future type. + * + * @since 2201.11.0 + */ +public final class BFutureSubType extends SubType implements DelegatedSubType { + + private final Bdd inner; + + private BFutureSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BFutureSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BFutureSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BFutureSubType other) { + return new BFutureSubType(other.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BFutureSubType otherFuture)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherFuture.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BFutureSubType otherFuture)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.intersect(otherFuture.inner)); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return cx.memoSubtypeIsEmpty(cx.mappingMemo, + (context, bdd) -> bddEvery(context, bdd, BMappingSubType::mappingFormulaIsEmpty), inner); + } + + @Override + public SubTypeData data() { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + public SubType inner() { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BFutureSubType other)) { + return false; + } + return Objects.equals(inner, other.inner); + } + + @Override + public int hashCode() { + return Objects.hash(inner); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BIntSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BIntSubType.java new file mode 100644 index 000000000000..30e123d338fe --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BIntSubType.java @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import static io.ballerina.runtime.api.constants.RuntimeConstants.INT_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.INT_MIN_VALUE; + +/** + * Runtime representation of a int subtype. + * + * @since 2201.11.0 + */ +public final class BIntSubType extends SubType { + + final SubTypeData data; + + private BIntSubType(SubTypeData data) { + super(data == AllOrNothing.ALL, data == AllOrNothing.NOTHING); + this.data = data; + } + + private static final BIntSubType ALL = new BIntSubType(AllOrNothing.ALL); + private static final BIntSubType NOTHING = new BIntSubType(AllOrNothing.NOTHING); + + public static BIntSubType createIntSubType(List values) { + Collections.sort(values); + List ranges = new ArrayList<>(); + long start = values.get(0); + long end = start; + for (int i = 1; i < values.size(); i++) { + long value = values.get(i); + if (value == end + 1) { + end = value; + } else { + ranges.add(new Range(start, end)); + start = value; + end = value; + } + } + ranges.add(new Range(start, end)); + return new BIntSubType(new IntSubTypeData(ranges.toArray(Range[]::new))); + } + + public static BIntSubType createIntSubType(long min, long max) { + assert min < max : "Invalid range"; + Range range = new Range(min, max); + Range[] ranges = {range}; + return new BIntSubType(new IntSubTypeData(ranges)); + } + + @Override + public SubType union(SubType otherSubType) { + BIntSubType other = (BIntSubType) otherSubType; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return this; + } else { + return other; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return other; + } else { + return this; + } + } + IntSubTypeData thisData = (IntSubTypeData) data; + IntSubTypeData otherData = (IntSubTypeData) other.data; + IntSubTypeData v = thisData.union(otherData); + Range[] resultRanges = v.ranges; + if (resultRanges.length == 1 && resultRanges[0].min == INT_MAX_VALUE && resultRanges[0].max == INT_MAX_VALUE) { + return ALL; + } + return new BIntSubType(v); + } + + @Override + public SubType intersect(SubType otherSubType) { + BIntSubType other = (BIntSubType) otherSubType; + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return other; + } else { + return NOTHING; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return this; + } else { + return NOTHING; + } + } + IntSubTypeData thisData = (IntSubTypeData) data; + IntSubTypeData otherData = (IntSubTypeData) other.data; + IntSubTypeData v = thisData.intersect(otherData); + Range[] resultRanges = v.ranges; + if (resultRanges.length == 0) { + return NOTHING; + } + return new BIntSubType(v); + } + + @Override + public SubType complement() { + if (this.data == AllOrNothing.ALL) { + return NOTHING; + } else if (this.data == AllOrNothing.NOTHING) { + return ALL; + } + IntSubTypeData intData = (IntSubTypeData) data; + return new BIntSubType(intData.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return data == AllOrNothing.NOTHING; + } + + @Override + public SubTypeData data() { + return data; + } + + public record Range(long min, long max) { + + } + + public static boolean intSubtypeContains(SubTypeData d, long n) { + if (!(d instanceof IntSubTypeData intSubTypeData)) { + return d == AllOrNothing.ALL; + } + return intSubTypeData.contains(n); + } + + public static final class IntSubTypeData implements SubTypeData { + + final Range[] ranges; + + private IntSubTypeData(Range range) { + this.ranges = new Range[]{range}; + } + + private IntSubTypeData(Range[] ranges) { + this.ranges = ranges; + } + + public List values() { + List values = new ArrayList<>(); + for (Range range : ranges) { + for (long i = range.min; i <= range.max; i++) { + values.add(i); + } + } + return values; + } + + public long max() { + return ranges[ranges.length - 1].max; + } + + boolean isRangeOverlap(Range range) { + IntSubTypeData subTypeData = intersect(new IntSubTypeData(range)); + return subTypeData.ranges.length != 0; + } + + private boolean contains(long n) { + for (Range r : ranges) { + if (r.min <= n && n <= r.max) { + return true; + } + } + return false; + } + + private IntSubTypeData union(IntSubTypeData other) { + List result = new ArrayList<>(); + int i1 = 0; + int i2 = 0; + Range[] v1 = this.ranges; + Range[] v2 = other.ranges; + int len1 = ranges.length; + int len2 = other.ranges.length; + while (true) { + if (i1 >= len1) { + if (i2 >= len2) { + break; + } + rangeUnionPush(result, v2[i2]); + i2++; + } else if (i2 >= len2) { + rangeUnionPush(result, v1[i1]); + i1++; + } else { + Range r1 = v1[i1]; + Range r2 = v2[i2]; + RangeOpResult combined = rangeUnion(r1, r2); + switch (combined.tag) { + case OVERLAP -> { + rangeUnionPush(result, combined.range); + i1++; + i2++; + } + case BEFORE -> { + rangeUnionPush(result, r1); + i1++; + } + case AFTER -> { + rangeUnionPush(result, r2); + i2++; + } + } + } + } + return new IntSubTypeData(result.toArray(Range[]::new)); + } + + IntSubTypeData intersect(IntSubTypeData other) { + List result = new ArrayList<>(); + int i1 = 0; + int i2 = 0; + Range[] v1 = this.ranges; + Range[] v2 = other.ranges; + int len1 = ranges.length; + int len2 = other.ranges.length; + while (true) { + if (i1 >= len1 || i2 >= len2) { + break; + } + Range r1 = v1[i1]; + Range r2 = v2[i2]; + RangeOpResult combined = rangeIntersect(r1, r2); + switch (combined.tag) { + case OVERLAP -> { + rangeUnionPush(result, combined.range); + i1++; + i2++; + } + case BEFORE -> i1++; + case AFTER -> i2++; + } + } + return new IntSubTypeData(result.toArray(Range[]::new)); + } + + IntSubTypeData complement() { + List result = new ArrayList<>(); + Range[] v = this.ranges; + int len = v.length; + long min = v[0].min; + if (min > INT_MIN_VALUE) { + result.add(new Range(INT_MIN_VALUE, min - 1)); + } + for (int i = 1; i < len; i++) { + result.add(new Range(v[i - 1].max + 1, v[i].min - 1)); + } + long max = v[v.length - 1].max; + if (max < INT_MAX_VALUE) { + result.add(new Range(max + 1, INT_MAX_VALUE)); + } + return new IntSubTypeData(result.toArray(Range[]::new)); + } + + private static void rangeUnionPush(List ranges, Range next) { + int lastIndex = ranges.size() - 1; + if (lastIndex < 0) { + ranges.add(next); + return; + } + RangeOpResult result = rangeUnion(ranges.get(lastIndex), next); + if (result.tag == RangeOpResultTag.OVERLAP) { + ranges.set(lastIndex, result.range); + } else { + ranges.add(next); + } + } + + private static RangeOpResult rangeIntersect(Range r1, Range r2) { + if (r1.max < r2.min) { + return new RangeOpResult(RangeOpResultTag.BEFORE, null); + } + if (r2.max < r1.min) { + return new RangeOpResult(RangeOpResultTag.AFTER, null); + } + return new RangeOpResult(RangeOpResultTag.OVERLAP, + new Range(Math.max(r1.min, r2.min), Math.min(r1.max, r2.max))); + } + + enum RangeOpResultTag { + BEFORE, + OVERLAP, + AFTER, + } + + record RangeOpResult(RangeOpResultTag tag, Range range) { + + } + + private static RangeOpResult rangeUnion(Range r1, Range r2) { + if (r1.max < r2.min) { + if (r1.max + 1 != r2.min) { + return new RangeOpResult(RangeOpResultTag.BEFORE, null); + } + } + if (r2.max < r1.min) { + if (r1.max + 1 != r2.min) { + return new RangeOpResult(RangeOpResultTag.AFTER, null); + } + } + return new RangeOpResult(RangeOpResultTag.OVERLAP, + new Range(Math.min(r1.min, r2.min), Math.max(r1.max, r2.max))); + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListProj.java new file mode 100644 index 000000000000..adcd59d67c89 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListProj.java @@ -0,0 +1,214 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddAllOrNothing; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Conjunction; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Pair; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Builder.getRoCellContaining; +import static io.ballerina.runtime.api.types.semtype.Conjunction.and; +import static io.ballerina.runtime.api.types.semtype.Core.cellInnerVal; +import static io.ballerina.runtime.api.types.semtype.Core.diff; +import static io.ballerina.runtime.api.types.semtype.Core.getComplexSubtypeData; +import static io.ballerina.runtime.api.types.semtype.Core.isEmpty; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; +import static io.ballerina.runtime.api.types.semtype.Core.isNothingSubtype; +import static io.ballerina.runtime.api.types.semtype.Core.union; +import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeContains; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.fixedArrayAnyEmpty; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.listIntersectWith; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.listMemberAtInnerVal; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.listSampleTypes; +import static io.ballerina.runtime.internal.types.semtype.BListSubType.listSamples; + +/** + * utility class for list type projection. + * + * @since 2201.11.0 + */ +public final class BListProj { + + private BListProj() { + } + + public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { + if (t.some() == 0) { + return t == Builder.getListType() ? Builder.getValType() : Builder.getNeverType(); + } else { + SubTypeData keyData = Core.intSubtype(k); + if (isNothingSubtype(keyData)) { + return Builder.getNeverType(); + } + return listProjBddInnerVal(cx, keyData, (Bdd) getComplexSubtypeData(t, BasicTypeCode.BT_LIST), null, + null); + } + } + + private static SemType listProjBddInnerVal(Context cx, SubTypeData k, Bdd b, Conjunction pos, Conjunction neg) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? listProjPathInnerVal(cx, k, pos, neg) : Builder.getNeverType(); + } else { + BddNode bddNode = (BddNode) b; + return union(listProjBddInnerVal(cx, k, bddNode.left(), and(bddNode.atom(), pos), neg), + union(listProjBddInnerVal(cx, k, bddNode.middle(), pos, neg), + listProjBddInnerVal(cx, k, bddNode.right(), pos, and(bddNode.atom(), neg)))); + } + } + + private static SemType listProjPathInnerVal(Context cx, SubTypeData k, Conjunction pos, Conjunction neg) { + FixedLengthArray members; + SemType rest; + if (pos == null) { + members = FixedLengthArray.empty(); + rest = Builder.getRwCellContaining(cx.env, union(Builder.getValType(), Builder.getUndefType())); + } else { + // combine all the positive tuples using intersection + ListAtomicType lt = cx.listAtomType(pos.atom()); + members = lt.members(); + rest = lt.rest(); + Conjunction p = pos.next(); + // the neg case is in case we grow the array in listInhabited + if (p != null || neg != null) { + members = members.shallowCopy(); + } + + while (true) { + if (p == null) { + break; + } else { + Atom d = p.atom(); + p = p.next(); + lt = cx.listAtomType(d); + Pair + intersected = listIntersectWith(cx.env, members, rest, lt.members(), lt.rest()); + if (intersected == null) { + return Builder.getNeverType(); + } + members = intersected.first(); + rest = intersected.second(); + } + } + if (fixedArrayAnyEmpty(cx, members)) { + return Builder.getNeverType(); + } + // Ensure that we can use isNever on rest in listInhabited + if (!isNever(cellInnerVal(rest)) && isEmpty(cx, rest)) { + rest = getRoCellContaining(cx.env, Builder.getNeverType()); + } + } + Integer[] indices = listSamples(cx, members, rest, neg); + Pair projSamples = listProjSamples(indices, k); + indices = projSamples.first(); + Pair sampleTypes = listSampleTypes(cx, members, rest, indices); + return listProjExcludeInnerVal(cx, projSamples.first(), + projSamples.second(), + sampleTypes.first(), + sampleTypes.second(), neg); + } + + private static SemType listProjExcludeInnerVal(Context cx, Integer[] indices, Integer[] keyIndices, + SemType[] memberTypes, int nRequired, Conjunction neg) { + SemType p = Builder.getNeverType(); + if (neg == null) { + int len = memberTypes.length; + for (int k : keyIndices) { + if (k < len) { + p = union(p, cellInnerVal(memberTypes[k])); + } + } + } else { + final ListAtomicType nt = cx.listAtomType(neg.atom()); + if (nRequired > 0 && isNever(listMemberAtInnerVal(nt.members(), nt.rest(), indices[nRequired - 1]))) { + return listProjExcludeInnerVal(cx, indices, keyIndices, memberTypes, nRequired, neg.next()); + } + int negLen = nt.members().fixedLength(); + if (negLen > 0) { + int len = memberTypes.length; + if (len < indices.length && indices[len] < negLen) { + return listProjExcludeInnerVal(cx, indices, keyIndices, memberTypes, nRequired, neg.next()); + } + for (int i = nRequired; i < memberTypes.length; i++) { + if (indices[i] >= negLen) { + break; + } + SemType[] t = Arrays.copyOfRange(memberTypes, 0, i); + p = union(p, listProjExcludeInnerVal(cx, indices, keyIndices, t, nRequired, neg.next())); + } + } + for (int i = 0; i < memberTypes.length; i++) { + SemType d = + diff(cellInnerVal(memberTypes[i]), listMemberAtInnerVal(nt.members(), nt.rest(), indices[i])); + if (!Core.isEmpty(cx, d)) { + SemType[] t = memberTypes.clone(); + t[i] = Builder.getRwCellContaining(cx.env, d); + // We need to make index i be required + p = union(p, listProjExcludeInnerVal(cx, indices, keyIndices, t, Integer.max(nRequired, i + 1), + neg.next())); + } + } + } + return p; + } + + private static Pair listProjSamples(Integer[] indices, SubTypeData k) { + List> v = new ArrayList<>(); + for (int i : indices) { + v.add(Pair.from(i, intSubtypeContains(k, i))); + } + if (k instanceof BIntSubType.IntSubTypeData intSubtype) { + for (BIntSubType.Range range : intSubtype.ranges) { + long max = range.max(); + if (range.max() >= 0) { + v.add(Pair.from((int) max, true)); + int min = Integer.max(0, (int) range.min()); + if (min < max) { + v.add(Pair.from(min, true)); + } + } + } + } + v.sort(Comparator.comparingInt(Pair::first)); + List indices1 = new ArrayList<>(); + List keyIndices = new ArrayList<>(); + for (var ib : v) { + if (indices1.isEmpty() || !Objects.equals(ib.first(), indices1.get(indices1.size() - 1))) { + if (ib.second()) { + keyIndices.add(indices1.size()); + } + indices1.add(ib.first()); + } + } + return Pair.from(indices1.toArray(Integer[]::new), keyIndices.toArray(Integer[]::new)); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListSubType.java new file mode 100644 index 000000000000..bb3bbec3769a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BListSubType.java @@ -0,0 +1,462 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddAllOrNothing; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Conjunction; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.Pair; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; +import static io.ballerina.runtime.api.types.semtype.Core.cellInner; +import static io.ballerina.runtime.api.types.semtype.Core.cellInnerVal; +import static io.ballerina.runtime.api.types.semtype.Core.getCellContainingInnerVal; +import static io.ballerina.runtime.api.types.semtype.Core.intersectCellMemberSemTypes; +import static io.ballerina.runtime.internal.types.semtype.BIntSubType.intSubtypeContains; + +/** + * Runtime representation of a subtype of list type. + * + * @since 2201.11.0 + */ +public class BListSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BListSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BListSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BListSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BListSubType bList) { + return new BListSubType(bList.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BListSubType otherList)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherList.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BListSubType otherList)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherList.inner)); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public SubType diff(SubType other) { + if (!(other instanceof BListSubType otherList)) { + throw new IllegalArgumentException("diff of different subtypes"); + } + return createDelegate(inner.diff(otherList.inner)); + } + + @Override + public boolean isEmpty(Context cx) { + return cx.memoSubtypeIsEmpty(cx.listMemo, + (context, bdd) -> bddEvery(context, bdd, BListSubType::listFormulaIsEmpty), inner); + } + + static boolean listFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + FixedLengthArray members; + SemType rest; + if (pos == null) { + ListAtomicType atom = Builder.getListAtomicInner(); + members = atom.members(); + rest = atom.rest(); + } else { + // combine all the positive tuples using intersection + ListAtomicType lt = cx.listAtomType(pos.atom()); + members = lt.members(); + rest = lt.rest(); + Conjunction p = pos.next(); + // the neg case is in case we grow the array in listInhabited + if (p != null || neg != null) { + members = members.shallowCopy(); + } + while (true) { + if (p == null) { + break; + } else { + Atom d = p.atom(); + p = p.next(); + lt = cx.listAtomType(d); + Pair + intersected = listIntersectWith(cx.env, members, rest, lt.members(), lt.rest()); + if (intersected == null) { + return true; + } + members = intersected.first(); + rest = intersected.second(); + } + } + if (fixedArrayAnyEmpty(cx, members)) { + return true; + } + } + Integer[] indices = listSamples(cx, members, rest, neg); + Pair sampleTypes = listSampleTypes(cx, members, rest, indices); + return !listInhabited(cx, indices, sampleTypes.first(), sampleTypes.second(), neg); + } + + // This function determines whether a list type P & N is inhabited. + // where P is a positive list type and N is a list of negative list types. + // With just straightforward fixed-length tuples we can consider every index of the tuple. + // But we cannot do this in general because of rest types and fixed length array types e.g. `byte[10000000]`. + // So we consider instead a collection of indices that is sufficient for us to determine inhabitation, + // given the types of P and N. + // `indices` is this list of sample indices: these are indicies into members of the list type. + // We don't represent P directly. Instead P is represented by `memberTypes` and `nRequired`: + // `memberTypes[i]` is the type that P gives to `indices[i]`; + // `nRequired` is the number of members of `memberTypes` that are required by P. + // `neg` represents N. + private static boolean listInhabited(Context cx, Integer[] indices, SemType[] memberTypes, int nRequired, + Conjunction neg) { + if (neg == null) { + return true; + } else { + final ListAtomicType nt = cx.listAtomType(neg.atom()); + if (nRequired > 0 && Core.isNever(listMemberAtInnerVal(nt.members(), nt.rest(), indices[nRequired - 1]))) { + // Skip this negative if it is always shorter than the minimum required by the positive + return listInhabited(cx, indices, memberTypes, nRequired, neg.next()); + } + // Consider cases we can avoid this negative by having a sufficiently short list + int negLen = nt.members().fixedLength(); + if (negLen > 0) { + int len = memberTypes.length; + // If we have isEmpty(T1 & S1) or isEmpty(T2 & S2) then we have [T1, T2] / [S1, S2] = [T1, T2]. + // Therefore, we can skip the negative + for (int i = 0; i < len; i++) { + int index = indices[i]; + if (index >= negLen) { + break; + } + SemType negMemberType = listMemberAt(nt.members(), nt.rest(), index); + SemType common = Core.intersect(memberTypes[i], negMemberType); + if (Core.isEmpty(cx, common)) { + return listInhabited(cx, indices, memberTypes, nRequired, neg.next()); + } + } + // Consider cases we can avoid this negative by having a sufficiently short list + if (len < indices.length && indices[len] < negLen) { + return listInhabited(cx, indices, memberTypes, nRequired, neg.next()); + } + for (int i = nRequired; i < memberTypes.length; i++) { + if (indices[i] >= negLen) { + break; + } + // TODO: avoid creating new arrays here, maybe use an object pool for this + // -- Or use a copy on write array? + SemType[] t = Arrays.copyOfRange(memberTypes, 0, i); + if (listInhabited(cx, indices, t, nRequired, neg.next())) { + return true; + } + } + } + // Now we need to explore the possibility of shapes with length >= neglen + // This is the heart of the algorithm. + // For [v0, v1] not to be in [t0,t1], there are two possibilities + // (1) v0 is not in t0, or + // (2) v1 is not in t1 + // Case (1) + // For v0 to be in s0 but not t0, d0 must not be empty. + // We must then find a [v0,v1] satisfying the remaining negated tuples, + // such that v0 is in d0. + // SemType d0 = diff(s[0], t[0]); + // if !isEmpty(cx, d0) && tupleInhabited(cx, [d0, s[1]], neg.rest) { + // return true; + // } + // Case (2) + // For v1 to be in s1 but not t1, d1 must not be empty. + // We must then find a [v0,v1] satisfying the remaining negated tuples, + // such that v1 is in d1. + // SemType d1 = diff(s[1], t[1]); + // return !isEmpty(cx, d1) && tupleInhabited(cx, [s[0], d1], neg.rest); + // We can generalize this to tuples of arbitrary length. + for (int i = 0; i < memberTypes.length; i++) { + SemType d = Core.diff(memberTypes[i], listMemberAt(nt.members(), nt.rest(), indices[i])); + if (!Core.isEmpty(cx, d)) { + SemType[] t = memberTypes.clone(); + t[i] = d; + // We need to make index i be required + if (listInhabited(cx, indices, t, Integer.max(nRequired, i + 1), neg.next())) { + return true; + } + } + } + // This is correct for length 0, because we know that the length of the + // negative is 0, and [] - [] is empty. + return false; + } + } + + public static Pair listSampleTypes(Context cx, FixedLengthArray members, + SemType rest, Integer[] indices) { + List memberTypes = new ArrayList<>(indices.length); + int nRequired = 0; + for (int i = 0; i < indices.length; i++) { + int index = indices[i]; + SemType t = getCellContainingInnerVal(cx.env, listMemberAt(members, rest, index)); + if (Core.isEmpty(cx, t)) { + break; + } + memberTypes.add(t); + if (index < members.fixedLength()) { + nRequired = i + 1; + } + } + SemType[] buffer = new SemType[memberTypes.size()]; + return Pair.from(memberTypes.toArray(buffer), nRequired); + } + + // Return a list of sample indices for use as second argument of `listInhabited`. + // The positive list type P is represented by `members` and `rest`. + // The negative list types N are represented by `neg` + // The `indices` list (first member of return value) is constructed in two stages. + // First, the set of all non-negative integers is partitioned so that two integers are + // in different partitions if they get different types as an index in P or N. + // Second, we choose a number of samples from each partition. It doesn't matter + // which sample we choose, but (this is the key point) we need at least as many samples + // as there are negatives in N, so that for each negative we can freely choose a type for the sample + // to avoid being matched by that negative. + public static Integer[] listSamples(Context cx, FixedLengthArray members, SemType rest, Conjunction neg) { + int maxInitialLength = members.initial().length; + List fixedLengths = new ArrayList<>(); + fixedLengths.add(members.fixedLength()); + Conjunction tem = neg; + int nNeg = 0; + while (true) { + if (tem != null) { + ListAtomicType lt = cx.listAtomType(tem.atom()); + FixedLengthArray m = lt.members(); + maxInitialLength = Integer.max(maxInitialLength, m.initial().length); + if (m.fixedLength() > maxInitialLength) { + fixedLengths.add(m.fixedLength()); + } + nNeg += 1; + tem = tem.next(); + } else { + break; + } + } + Collections.sort(fixedLengths); + // `boundaries` partitions the non-negative integers + // Construct `boundaries` from `fixedLengths` and `maxInitialLength` + // An index b is a boundary point if indices < b are different from indices >= b + //int[] boundaries = from int i in 1 ... maxInitialLength select i; + List boundaries = new ArrayList<>(fixedLengths.size()); + for (int i = 1; i <= maxInitialLength; i++) { + boundaries.add(i); + } + for (int n : fixedLengths) { + // this also removes duplicates + if (boundaries.isEmpty() || n > boundaries.get(boundaries.size() - 1)) { + boundaries.add(n); + } + } + // Now construct the list of indices by taking nNeg samples from each partition. + List indices = new ArrayList<>(boundaries.size()); + int lastBoundary = 0; + if (nNeg == 0) { + // this is needed for when this is used in listProj + nNeg = 1; + } + for (int b : boundaries) { + int segmentLength = b - lastBoundary; + // Cannot have more samples than are in the parition. + int nSamples = Integer.min(segmentLength, nNeg); + for (int i = b - nSamples; i < b; i++) { + indices.add(i); + } + lastBoundary = b; + } + for (int i = 0; i < nNeg; i++) { + // Be careful to avoid integer overflow. + if (lastBoundary > Integer.MAX_VALUE - i) { + break; + } + indices.add(lastBoundary + i); + } + Integer[] arr = new Integer[indices.size()]; + return indices.toArray(arr); + } + + public static boolean fixedArrayAnyEmpty(Context cx, FixedLengthArray array) { + for (var t : array.initial()) { + if (Core.isEmpty(cx, t)) { + return true; + } + } + return false; + } + + public static Pair listIntersectWith(Env env, FixedLengthArray members1, SemType rest1, + FixedLengthArray members2, SemType rest2) { + + if (listLengthsDisjoint(members1, rest1, members2, rest2)) { + return null; + } + int max = Integer.max(members1.fixedLength(), members2.fixedLength()); + SemType[] initial = new SemType[max]; + for (int i = 0; i < max; i++) { + initial[i] = + intersectCellMemberSemTypes(env, listMemberAt(members1, rest1, i), + listMemberAt(members2, rest2, i)); + } + return Pair.from(FixedLengthArray.from(initial, + Integer.max(members1.fixedLength(), members2.fixedLength())), + intersectCellMemberSemTypes(env, rest1, rest2)); + } + + private static boolean listLengthsDisjoint(FixedLengthArray members1, SemType rest1, + FixedLengthArray members2, SemType rest2) { + int len1 = members1.fixedLength(); + int len2 = members2.fixedLength(); + if (len1 < len2) { + return Core.isNever(cellInnerVal(rest1)); + } + if (len2 < len1) { + return Core.isNever(cellInnerVal(rest2)); + } + return false; + } + + private static SemType listMemberAt(FixedLengthArray fixedArray, SemType rest, int index) { + if (index < fixedArray.fixedLength()) { + return fixedArrayGet(fixedArray, index); + } + return rest; + } + + private static SemType fixedArrayGet(FixedLengthArray members, int index) { + int memberLen = members.initial().length; + int i = Integer.min(index, memberLen - 1); + return members.initial()[i]; + } + + public static SemType listMemberAtInnerVal(FixedLengthArray fixedArray, SemType rest, int index) { + return cellInnerVal(listMemberAt(fixedArray, rest, index)); + } + + public static SemType bddListMemberTypeInnerVal(Context cx, Bdd b, SubTypeData key, SemType accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : Builder.getNeverType(); + } else { + BddNode bddNode = (BddNode) b; + return Core.union(bddListMemberTypeInnerVal(cx, bddNode.left(), key, + Core.intersect(listAtomicMemberTypeInnerVal(cx.listAtomType(bddNode.atom()), key), accum)), + Core.union(bddListMemberTypeInnerVal(cx, bddNode.middle(), key, accum), + bddListMemberTypeInnerVal(cx, bddNode.right(), key, accum))); + } + } + + private static SemType listAtomicMemberTypeInnerVal(ListAtomicType atomic, SubTypeData key) { + return Core.diff(listAtomicMemberTypeInner(atomic, key), Builder.getUndefType()); + } + + private static SemType listAtomicMemberTypeInner(ListAtomicType atomic, SubTypeData key) { + return listAtomicMemberTypeAtInner(atomic.members(), atomic.rest(), key); + } + + static SemType listAtomicMemberTypeAtInner(FixedLengthArray fixedArray, SemType rest, SubTypeData key) { + if (key instanceof BIntSubType.IntSubTypeData intSubtype) { + SemType m = Builder.getNeverType(); + int initLen = fixedArray.initial().length; + int fixedLen = fixedArray.fixedLength(); + if (fixedLen != 0) { + for (int i = 0; i < initLen; i++) { + if (intSubtypeContains(key, i)) { + m = Core.union(m, cellInner(fixedArrayGet(fixedArray, i))); + } + } + if (intSubtype.isRangeOverlap(new BIntSubType.Range(initLen, fixedLen - 1))) { + m = Core.union(m, cellInner(fixedArrayGet(fixedArray, fixedLen - 1))); + } + } + if (fixedLen == 0 || intSubtype.max() > fixedLen - 1) { + m = Core.union(m, cellInner(rest)); + } + return m; + } + SemType m = cellInner(rest); + if (fixedArray.fixedLength() > 0) { + for (SemType ty : fixedArray.initial()) { + m = Core.union(m, cellInner(ty)); + } + } + return m; + } + + @Override + public SubTypeData data() { + return inner(); + } + + @Override + public Bdd inner() { + return inner; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BListSubType that)) { + return false; + } + return Objects.equals(inner, that.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingProj.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingProj.java new file mode 100644 index 000000000000..94f344aa1e85 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingProj.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddAllOrNothing; +import io.ballerina.runtime.api.types.semtype.BddNode; +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 java.util.ArrayList; +import java.util.List; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_MAPPING; +import static io.ballerina.runtime.api.types.semtype.Core.diff; +import static io.ballerina.runtime.api.types.semtype.Core.getComplexSubtypeData; +import static io.ballerina.runtime.api.types.semtype.Core.isNothingSubtype; +import static io.ballerina.runtime.api.types.semtype.Core.stringSubtype; + +/** + * Utility class for doing mapping type projection. + * + * @since 2201.11.0 + */ +public final class BMappingProj { + + private BMappingProj() { + } + + public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { + return diff(mappingMemberTypeInner(cx, t, k), Builder.getUndefType()); + } + + // This computes the spec operation called "member type of K in T", + // for when T is a subtype of mapping, and K is either `string` or a singleton string. + // This is what Castagna calls projection. + public static SemType mappingMemberTypeInner(Context cx, SemType t, SemType k) { + if (t.some() == 0) { + return (t.all() & Builder.getMappingType().all()) != 0 ? Builder.getValType() : Builder.getUndefType(); + } else { + SubTypeData keyData = stringSubtype(k); + if (isNothingSubtype(keyData)) { + return Builder.getUndefType(); + } + return bddMappingMemberTypeInner(cx, (Bdd) getComplexSubtypeData(t, BT_MAPPING), keyData, + Builder.getInnerType()); + } + } + + static SemType bddMappingMemberTypeInner(Context cx, Bdd b, SubTypeData key, SemType accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : Builder.getNeverType(); + } else { + BddNode bdd = (BddNode) b; + return Core.union( + bddMappingMemberTypeInner(cx, bdd.left(), key, + Core.intersect(mappingAtomicMemberTypeInner(cx.mappingAtomType(bdd.atom()), key), + accum)), + Core.union(bddMappingMemberTypeInner(cx, bdd.middle(), key, accum), + bddMappingMemberTypeInner(cx, bdd.right(), key, accum))); + } + } + + static SemType mappingAtomicMemberTypeInner(MappingAtomicType atomic, SubTypeData key) { + SemType memberType = null; + for (SemType ty : mappingAtomicApplicableMemberTypesInner(atomic, key)) { + if (memberType == null) { + memberType = ty; + } else { + memberType = Core.union(memberType, ty); + } + } + return memberType == null ? Builder.getUndefType() : memberType; + } + + static List mappingAtomicApplicableMemberTypesInner(MappingAtomicType atomic, SubTypeData key) { + List types = new ArrayList<>(atomic.types().length); + for (SemType t : atomic.types()) { + types.add(Core.cellInner(t)); + } + + List memberTypes = new ArrayList<>(); + SemType rest = Core.cellInner(atomic.rest()); + if (isAllSubtype(key)) { + memberTypes.addAll(types); + memberTypes.add(rest); + } else { + BStringSubType.StringSubtypeListCoverage coverage = + ((BStringSubType.StringSubTypeData) key).stringSubtypeListCoverage(atomic.names()); + for (int index : coverage.indices()) { + memberTypes.add(types.get(index)); + } + if (!coverage.isSubType()) { + memberTypes.add(rest); + } + } + return memberTypes; + } + + static boolean isAllSubtype(SubTypeData d) { + return d == AllOrNothing.ALL; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingSubType.java new file mode 100644 index 000000000000..bc56809a3c66 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BMappingSubType.java @@ -0,0 +1,221 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Conjunction; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.FieldPair; +import io.ballerina.runtime.api.types.semtype.FieldPairs; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Arrays; +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; + +/** + * Runtime representation of a subtype of mapping type. + * + * @since 2201.11.0 + */ +public class BMappingSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BMappingSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BMappingSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BMappingSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BMappingSubType bMapping) { + return new BMappingSubType(bMapping.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public Bdd inner() { + return inner; + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BMappingSubType otherMapping)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherMapping.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BMappingSubType otherMapping)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherMapping.inner)); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return cx.memoSubtypeIsEmpty(cx.mappingMemo, + (context, bdd) -> bddEvery(context, bdd, BMappingSubType::mappingFormulaIsEmpty), inner); + } + + static boolean mappingFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { + MappingAtomicType combined; + if (posList == null) { + combined = Builder.getMappingAtomicInner(); + } else { + // combine all the positive atoms using intersection + combined = cx.mappingAtomType(posList.atom()); + Conjunction p = posList.next(); + while (true) { + if (p == null) { + break; + } else { + MappingAtomicType m = + combined.intersectMapping(cx.env, cx.mappingAtomType(p.atom())); + if (m == null) { + return true; + } else { + combined = m; + } + p = p.next(); + } + } + for (SemType t : combined.types()) { + if (Core.isEmpty(cx, t)) { + return true; + } + } + + } + return !mappingInhabited(cx, combined, negList); + } + + private static boolean mappingInhabited(Context cx, MappingAtomicType pos, Conjunction negList) { + if (negList == null) { + return true; + } else { + MappingAtomicType neg = cx.mappingAtomType(negList.atom()); + + if (!Core.isEmpty(cx, Core.diff(pos.rest(), neg.rest()))) { + return mappingInhabited(cx, pos, negList.next()); + } + for (FieldPair fieldPair : new FieldPairs(pos, neg)) { + SemType intersect = Core.intersect(fieldPair.type1(), fieldPair.type2()); + // if types of at least one field are disjoint, the neg atom will not contribute to the next iteration. + // Therefore, we can skip the current neg atom. + // i.e. if we have isEmpty(T1 & S1) or isEmpty(T2 & S2) then, + // record { T1 f1; T2 f2; } / record { S1 f1; S2 f2; } = record { T1 f1; T2 f2; } + if (Core.isEmpty(cx, intersect)) { + return mappingInhabited(cx, pos, negList.next()); + } + + SemType d = Core.diff(fieldPair.type1(), fieldPair.type2()); + if (!Core.isEmpty(cx, d)) { + MappingAtomicType mt; + if (fieldPair.index1() == null) { + // the posType came from the rest type + mt = insertField(pos, fieldPair.name(), d); + } else { + SemType[] posTypes = pos.types().clone(); + posTypes[fieldPair.index1()] = d; + mt = new MappingAtomicType(pos.names(), posTypes, pos.rest()); + } + if (mappingInhabited(cx, mt, negList.next())) { + return true; + } + } + } + return false; + } + } + + private static MappingAtomicType insertField(MappingAtomicType m, String name, SemType t) { + String[] orgNames = m.names(); + String[] names = shallowCopyStrings(orgNames, orgNames.length + 1); + SemType[] orgTypes = m.types(); + SemType[] types = shallowCopySemTypes(orgTypes, orgTypes.length + 1); + int i = orgNames.length; + while (true) { + if (i == 0 || Common.codePointCompare(names[i - 1], name)) { + names[i] = name; + types[i] = t; + break; + } + names[i] = names[i - 1]; + types[i] = types[i - 1]; + i -= 1; + } + return new MappingAtomicType(names, types, m.rest()); + } + + static SemType[] shallowCopySemTypes(SemType[] v, int newLength) { + return Arrays.copyOf(v, newLength); + } + + private static String[] shallowCopyStrings(String[] v, int newLength) { + return Arrays.copyOf(v, newLength); + } + + @Override + public SubTypeData data() { + return inner(); + } + + @Override + public SubType diff(SubType other) { + if (!(other instanceof BMappingSubType otherList)) { + throw new IllegalArgumentException("diff of different subtypes"); + } + return createDelegate(inner.diff(otherList.inner)); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BMappingSubType that)) { + return false; + } + return Objects.equals(inner, that.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BObjectSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BObjectSubType.java new file mode 100644 index 000000000000..de6516a4086a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BObjectSubType.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEveryPositive; + +/** + * Runtime representation of a subtype of object type. + * + * @since 2201.11.0 + */ +public final class BObjectSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BObjectSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BObjectSubType otherObject)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherObject.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BObjectSubType otherObject)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherObject.inner)); + } + + @Override + public SubType complement() { + return createDelegate(inner.complement()); + } + + @Override + public boolean isEmpty(Context cx) { + return cx.memoSubtypeIsEmpty(cx.mappingMemo, + (context, bdd) -> bddEveryPositive(context, bdd, null, null, BMappingSubType::mappingFormulaIsEmpty), + inner); + } + + @Override + public SubTypeData data() { + throw new UnsupportedOperationException("Method not implemented"); + } + + public static BObjectSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BObjectSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BObjectSubType bMapping) { + return new BObjectSubType(bMapping.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public Bdd inner() { + throw new UnsupportedOperationException("Method not implemented"); + } + + @Override + public SubType diff(SubType other) { + if (!(other instanceof BObjectSubType otherObject)) { + throw new IllegalArgumentException("diff of different subtypes"); + } + return createDelegate(inner.diff(otherObject.inner)); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BObjectSubType that)) { + return false; + } + return Objects.equals(inner, that.inner); + } + + @Override + public int hashCode() { + return Objects.hashCode(inner); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStreamSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStreamSubType.java new file mode 100644 index 000000000000..c666b54ce417 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStreamSubType.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; + +/** + * Runtime representation of a subtype of stream type. + * + * @since 2201.11.0 + */ +public class BStreamSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + + private BStreamSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BStreamSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BStreamSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BStreamSubType bStreamSubType) { + return new BStreamSubType(bStreamSubType.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BStreamSubType otherStream)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherStream.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BStreamSubType otherStream)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherStream.inner)); + } + + @Override + public SubType complement() { + return createDelegate(Builder.getListSubtypeTwoElement().diff(inner)); + } + + @Override + public boolean isEmpty(Context cx) { + Bdd b = inner; + // The goal of this is to ensure that listSubtypeIsEmpty call beneath does + // not get an empty posList, because it will interpret that + // as `[any|error...]` rather than `[any|error, any|error]`. + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getListSubtypeTwoElement()) : b; + return cx.memoSubtypeIsEmpty(cx.listMemo, + (context, bdd) -> bddEvery(context, bdd, BListSubType::listFormulaIsEmpty), b); + } + + @Override + public SubTypeData data() { + return inner(); + } + + @Override + public Bdd inner() { + return inner; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStringSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStringSubType.java new file mode 100644 index 000000000000..0527874f61f1 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BStringSubType.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Objects; + +/** + * Runtime representation of subtype of string type. + * + * @since 2201.11.0 + */ +public final class BStringSubType extends SubType { + + final SubTypeData data; + private static final BStringSubType ALL = new BStringSubType(AllOrNothing.ALL); + private static final BStringSubType NOTHING = new BStringSubType(AllOrNothing.NOTHING); + + private BStringSubType(SubTypeData data) { + super(data == AllOrNothing.ALL, data == AllOrNothing.NOTHING); + this.data = data; + } + + public static BStringSubType createStringSubType(boolean charsAllowed, String[] chars, boolean nonCharsAllowed, + String[] nonChars) { + if (chars.length == 0 && nonChars.length == 0) { + if (!charsAllowed && !nonCharsAllowed) { + return ALL; + } else if (charsAllowed && nonCharsAllowed) { + return NOTHING; + } + } + Arrays.sort(chars); + Arrays.sort(nonChars); + ValueData charValues = new ValueData(charsAllowed, chars); + ValueData nonCharValues = new ValueData(nonCharsAllowed, nonChars); + StringSubTypeData data = new StringSubTypeData(charValues, nonCharValues); + return new BStringSubType(data); + } + + @Override + public String toString() { + if (data instanceof StringSubTypeData stringSubTypeData) { + var chars = stringSubTypeData.chars; + var nonChars = stringSubTypeData.nonChars; + if (chars.allowed && chars.values.length > 0 && nonChars.allowed && nonChars.values.length == 0) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < chars.values.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(chars.values[i]); + } + return sb.toString(); + } else if (nonChars.allowed && nonChars.values.length > 0 && chars.allowed && chars.values.length == 0) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < nonChars.values.length; i++) { + if (i > 0) { + sb.append(", "); + } + sb.append(nonChars.values[i]); + } + return sb.toString(); + } + } + return "string"; + } + + @Override + public SubType union(SubType otherSubType) { + BStringSubType other = (BStringSubType) otherSubType; + // TODO: refactor + if (this.data instanceof AllOrNothing || other.data instanceof AllOrNothing) { + if (this.data == AllOrNothing.ALL) { + return this; + } else if (other.data == AllOrNothing.ALL) { + return other; + } else if (this.data == AllOrNothing.NOTHING) { + return other; + } else if (other.data == AllOrNothing.NOTHING) { + return this; + } + throw new IllegalStateException("unreachable"); + } + StringSubTypeData data = (StringSubTypeData) this.data; + StringSubTypeData otherData = (StringSubTypeData) other.data; + List chars = new ArrayList<>(); + boolean charsAllowed = data.chars.union(otherData.chars, chars); + List nonChars = new ArrayList<>(); + boolean nonCharsAllowed = data.nonChars.union(otherData.nonChars, nonChars); + return createStringSubType(charsAllowed, chars.toArray(String[]::new), nonCharsAllowed, + nonChars.toArray(String[]::new)); + } + + @Override + public SubType intersect(SubType otherSubtype) { + BStringSubType other = (BStringSubType) otherSubtype; + if (this.data instanceof AllOrNothing) { + if (this.data == AllOrNothing.ALL) { + return other; + } else { + return NOTHING; + } + } else if (other.data instanceof AllOrNothing) { + if (other.data == AllOrNothing.ALL) { + return this; + } else { + return NOTHING; + } + } + StringSubTypeData data = (StringSubTypeData) this.data; + StringSubTypeData otherData = (StringSubTypeData) other.data; + List chars = new ArrayList<>(); + boolean charsAllowed = data.chars.intersect(otherData.chars, chars); + List nonChars = new ArrayList<>(); + boolean nonCharsAllowed = data.nonChars.intersect(otherData.nonChars, nonChars); + return createStringSubType(charsAllowed, chars.toArray(String[]::new), nonCharsAllowed, + nonChars.toArray(String[]::new)); + } + + @Override + public SubType complement() { + if (data instanceof AllOrNothing) { + if (data == AllOrNothing.ALL) { + return NOTHING; + } else { + return ALL; + } + } + StringSubTypeData stringData = (StringSubTypeData) data; + ValueData chars = stringData.chars; + ValueData nonChars = stringData.nonChars; + return createStringSubType(!chars.allowed, chars.values, !nonChars.allowed, nonChars.values); + } + + @Override + public boolean isEmpty(Context cx) { + return data == AllOrNothing.NOTHING; + } + + @Override + public SubTypeData data() { + return data; + } + + static void stringListIntersect(String[] values, String[] target, List indices) { + int i1 = 0; + int i2 = 0; + int len1 = values.length; + int len2 = target.length; + while (true) { + if (i1 >= len1 || i2 >= len2) { + break; + } else { + switch (compareStrings(values[i1], target[i2])) { + case EQ: + indices.add(i1); + i1 += 1; + i2 += 1; + break; + case LT: + i1 += 1; + break; + case GT: + i2 += 1; + break; + default: + throw new AssertionError("Invalid comparison value!"); + } + } + } + } + + private static ComparisonResult compareStrings(String s1, String s2) { + return Objects.equals(s1, s2) ? ComparisonResult.EQ : + (Common.codePointCompare(s1, s2) ? ComparisonResult.LT : ComparisonResult.GT); + } + + private enum ComparisonResult { + EQ, + LT, + GT + } + + record StringSubTypeData(ValueData chars, ValueData nonChars) implements SubTypeData { + + StringSubtypeListCoverage stringSubtypeListCoverage(String[] values) { + List indices = new ArrayList<>(); + ValueData ch = chars(); + ValueData nonChar = nonChars(); + int stringConsts = 0; + if (ch.allowed) { + stringListIntersect(values, ch.values, indices); + stringConsts = ch.values.length; + } else if (ch.values.length == 0) { + for (int i = 0; i < values.length; i++) { + if (values[i].length() == 1) { + indices.add(i); + } + } + } + if (nonChar.allowed) { + stringListIntersect(values, nonChar.values, indices); + stringConsts += nonChar.values.length; + } else if (nonChar.values.length == 0) { + for (int i = 0; i < values.length; i++) { + if (values[i].length() != 1) { + indices.add(i); + } + } + } + int[] inds = indices.stream().mapToInt(i -> i).toArray(); + return new StringSubtypeListCoverage(stringConsts == indices.size(), inds); + } + } + + record StringSubtypeListCoverage(boolean isSubType, int[] indices) { + + } + + static final class ValueData extends EnumerableSubtypeData { + + private final boolean allowed; + private final String[] values; + + // NOTE: this assumes values are sorted + private ValueData(boolean allowed, String[] values) { + this.allowed = allowed; + this.values = values; + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public String[] values() { + return values; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTableSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTableSubType.java new file mode 100644 index 000000000000..91243d618228 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTableSubType.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEvery; + +/** + * Represents the subtype of a table type. + * + * @since 2201.11.0 + */ +public final class BTableSubType extends SubType implements DelegatedSubType { + + private final Bdd inner; + + private BTableSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BTableSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BTableSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BTableSubType other) { + return new BTableSubType(other.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BTableSubType otherTable)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherTable.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BTableSubType otherTable)) { + throw new IllegalArgumentException("intersect of different subtypes"); + } + return createDelegate(inner.intersect(otherTable.inner)); + } + + @Override + public SubType complement() { + return createDelegate(Builder.getListSubtypeThreeElement().diff(inner)); + } + + @Override + public boolean isEmpty(Context cx) { + Bdd b = inner; + // The goal of this is to ensure that listSubtypeIsEmpty call beneath does + // not get an empty posList, because it will interpret that + // as `(any|error)[]` rather than `[(map)[], any|error, any|error]`. + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getListSubtypeThreeElement()) : b; + return cx.memoSubtypeIsEmpty(cx.listMemo, + (context, bdd) -> bddEvery(context, bdd, BListSubType::listFormulaIsEmpty), b); + } + + @Override + public SubTypeData data() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public SubType inner() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BTableSubType other)) { + return false; + } + return inner.equals(other.inner); + } + + @Override + public int hashCode() { + return Objects.hash(inner); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypedescSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypedescSubType.java new file mode 100644 index 000000000000..b8b0e91212a3 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BTypedescSubType.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +import static io.ballerina.runtime.api.types.semtype.Bdd.bddEveryPositive; + +/** + * Represents the subtype of a typedesc type. + * + * @since 2201.11.0 + */ +public class BTypedescSubType extends SubType implements DelegatedSubType { + + private final Bdd inner; + + private BTypedescSubType(Bdd inner) { + super(inner.isAll(), inner.isNothing()); + this.inner = inner; + } + + public static BTypedescSubType createDelegate(SubType inner) { + if (inner instanceof Bdd bdd) { + return new BTypedescSubType(bdd); + } else if (inner.isAll() || inner.isNothing()) { + throw new IllegalStateException("unimplemented"); + } else if (inner instanceof BTypedescSubType other) { + return new BTypedescSubType(other.inner); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + if (!(other instanceof BTypedescSubType otherTypedesc)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.union(otherTypedesc.inner)); + } + + @Override + public SubType intersect(SubType other) { + if (!(other instanceof BTypedescSubType otherTypedesc)) { + throw new IllegalArgumentException("union of different subtypes"); + } + return createDelegate(inner.intersect(otherTypedesc.inner)); + } + + @Override + public SubType complement() { + return createDelegate(Builder.getBddSubtypeRo().diff(inner)); + } + + @Override + public boolean isEmpty(Context cx) { + Bdd b = inner; + // The goal of this is to ensure that mappingFormulaIsEmpty call in errorBddIsEmpty beneath + // does not get an empty posList, because it will interpret that + // as `map` rather than `readonly & map`. + b = b.posMaybeEmpty() ? (Bdd) b.intersect(Builder.getBddSubtypeRo()) : b; + return cx.memoSubtypeIsEmpty(cx.mappingMemo, BTypedescSubType::typedescBddIsEmpty, b); + } + + private static boolean typedescBddIsEmpty(Context cx, Bdd b) { + return bddEveryPositive(cx, b, null, null, BMappingSubType::mappingFormulaIsEmpty); + } + + @Override + public SubTypeData data() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public SubType inner() { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof BTypedescSubType other)) { + return false; + } + return Objects.equals(inner, other.inner); + } + + @Override + public int hashCode() { + return Objects.hash(inner); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BXmlSubType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BXmlSubType.java new file mode 100644 index 000000000000..35479680eb62 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BXmlSubType.java @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Conjunction; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.RecAtom; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Objects; + +/** + * Represents the subtype of an XML type. + * + * @since 2201.11.0 + */ +public class BXmlSubType extends SubType implements DelegatedSubType { + + public final Bdd inner; + private final int primitives; + + private BXmlSubType(Bdd inner, int primitives) { + super(false, false); + this.inner = inner; + this.primitives = primitives; + } + + public static BXmlSubType createDelegate(int primitives, SubType inner) { + if (inner instanceof Bdd bdd) { + return new BXmlSubType(bdd, primitives); + } else if (inner instanceof BXmlSubType bXml) { + return new BXmlSubType(bXml.inner, primitives); + } + throw new IllegalArgumentException("Unexpected inner type"); + } + + @Override + public SubType union(SubType other) { + BXmlSubType otherXml = (BXmlSubType) other; + int primitives = this.primitives() | otherXml.primitives(); + return createDelegate(primitives, inner.union(otherXml.inner)); + } + + @Override + public SubType intersect(SubType other) { + BXmlSubType otherXml = (BXmlSubType) other; + int primitives = this.primitives() & otherXml.primitives(); + return createDelegate(primitives, inner.intersect(otherXml.inner)); + } + + @Override + public SubType diff(SubType other) { + BXmlSubType otherXml = (BXmlSubType) other; + return diff(this, otherXml); + } + + private static SubType diff(BXmlSubType st1, BXmlSubType st2) { + int primitives = st1.primitives() & ~st2.primitives(); + return createDelegate(primitives, st1.inner.diff(st2.inner)); + } + + @Override + public SubType complement() { + return diff((BXmlSubType) XmlUtils.XML_SUBTYPE_TOP, this); + } + + @Override + public boolean isEmpty(Context cx) { + if (primitives() != 0) { + return false; + } + return xmlBddEmpty(cx); + } + + private boolean xmlBddEmpty(Context cx) { + return Bdd.bddEvery(cx, inner, BXmlSubType::xmlFormulaIsEmpty); + } + + private static boolean xmlFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + int allPosBits = collectAllPrimitives(pos) & XmlUtils.XML_PRIMITIVE_ALL_MASK; + return xmlHasTotalNegative(allPosBits, neg); + } + + private static boolean xmlHasTotalNegative(int allPosBits, Conjunction conjunction) { + if (allPosBits == 0) { + return true; + } + Conjunction n = conjunction; + while (n != null) { + if ((allPosBits & ~getIndex(n)) == 0) { + return true; + } + n = n.next(); + } + return false; + } + + private static int collectAllPrimitives(Conjunction conjunction) { + int bits = 0; + Conjunction current = conjunction; + while (current != null) { + bits &= getIndex(current); + current = current.next(); + } + return bits; + } + + private static int getIndex(Conjunction conjunction) { + var atom = conjunction.atom(); + assert atom instanceof RecAtom; + return atom.index(); + } + + @Override + public SubTypeData data() { + return this; + } + + @Override + public SubType inner() { + return this; + } + + int primitives() { + return primitives; + } + + Bdd bdd() { + return inner; + } + + @Override + public int hashCode() { + return Objects.hash(inner, primitives); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BXmlSubType other)) { + return false; + } + return Objects.equals(bdd(), other.bdd()) && primitives() == other.primitives(); + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddMemo.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddMemo.java new file mode 100644 index 000000000000..739117d179f5 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/BddMemo.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import java.util.Objects; +import java.util.Optional; + +/** + * Represents the memoization emptiness of a BDD used in Context. + * + * @since 2201.11.0 + */ +public final class BddMemo { + + public Status isEmpty; + + public BddMemo() { + this.isEmpty = Status.NULL; + } + + public enum Status { + // We know where this BDD is empty or not + TRUE, + FALSE, + // There is some recursive part in this type + LOOP, + CYCLIC, + // We are in the process of determining if this BDD is empty or not + PROVISIONAL, + // We just initialized the node, treated to be same as not having a memo + NULL + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof BddMemo bddMemo)) { + return false; + } + return isEmpty == bddMemo.isEmpty; + } + + @Override + public int hashCode() { + return Objects.hashCode(isEmpty); + } + + public Optional isEmpty() { + return switch (isEmpty) { + // Cyclic types are empty because we define types inductively + case TRUE, CYCLIC -> Optional.of(true); + case FALSE -> Optional.of(false); + case LOOP, PROVISIONAL -> { + // If it was provisional we came from a back edge + // Again we treat the loop part as empty + isEmpty = Status.LOOP; + yield Optional.of(true); + } + case NULL -> Optional.empty(); + }; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/CellAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/CellAtomicType.java new file mode 100644 index 000000000000..8e75f75af7b6 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/CellAtomicType.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.AtomicType; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.TypeAtom; + +import java.util.HashMap; +import java.util.Map; + +/** + * CellAtomicType node. + * + * @param ty Type "wrapped" by this cell + * @param mut Mutability of the cell + * @since 2201.11.0 + */ +public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicType { + + public CellAtomicType { + assert ty != null; + } + + public static CellAtomicType from(SemType ty, CellMutability mut) { + return CellAtomCache.get(ty, mut); + } + + public static CellAtomicType intersectCellAtomicType(CellAtomicType c1, CellAtomicType c2) { + SemType ty = Core.intersect(c1.ty(), c2.ty()); + CellMutability mut = min(c1.mut(), c2.mut()); + return CellAtomicType.from(ty, mut); + } + + private static CellMutability min(CellMutability m1, + CellMutability m2) { + return m1.compareTo(m2) <= 0 ? m1 : m2; + } + + public static CellAtomicType cellAtomType(Atom atom) { + return (CellAtomicType) ((TypeAtom) atom).atomicType(); + } + + public enum CellMutability { + CELL_MUT_NONE, + CELL_MUT_LIMITED, + CELL_MUT_UNLIMITED + } + + private static final class CellAtomCache { + + private static final Map NONE_CACHE = new HashMap<>(); + private static final Map LIMITED_CACHE = new HashMap<>(); + private static final Map UNLIMITED_CACHE = new HashMap<>(); + + private static CellAtomicType get(SemType semType, CellMutability mut) { + if (semType.some() != 0) { + return new CellAtomicType(semType, mut); + } + int key = semType.all(); + return switch (mut) { + case CELL_MUT_NONE -> NONE_CACHE.computeIfAbsent(key, (ignored) -> new CellAtomicType(semType, mut)); + case CELL_MUT_LIMITED -> + LIMITED_CACHE.computeIfAbsent(key, (ignored) -> new CellAtomicType(semType, mut)); + case CELL_MUT_UNLIMITED -> + UNLIMITED_CACHE.computeIfAbsent(key, (ignored) -> new CellAtomicType(semType, mut)); + }; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Common.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Common.java new file mode 100644 index 000000000000..8d057aa5e4ef --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Common.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +/** + * Utility class for various operations related to semantic types. + * + * @since 2201.11.0 + */ +public final class Common { + + private Common() { + } + + public static boolean codePointCompare(String s1, String s2) { + if (s1.equals(s2)) { + return false; + } + int len1 = s1.length(); + int len2 = s2.length(); + if (len1 < len2 && s2.substring(0, len1).equals(s1)) { + return true; + } + int cpCount1 = s1.codePointCount(0, len1); + int cpCount2 = s2.codePointCount(0, len2); + for (int cp = 0; cp < cpCount1 && cp < cpCount2; ) { + int codepoint1 = s1.codePointAt(cp); + int codepoint2 = s2.codePointAt(cp); + if (codepoint1 == codepoint2) { + cp++; + continue; + } + return codepoint1 < codepoint2; + } + return false; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DefinitionContainer.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DefinitionContainer.java new file mode 100644 index 000000000000..2b4373a2de5e --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DefinitionContainer.java @@ -0,0 +1,111 @@ +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.function.Supplier; + +/** + * Container used to maintain concurrency invariants when creating a potentially recursive semtype. + * + * It maintains fallowing invariants. + * 1. When the type is being defined only the thread that is defining the type may proceed + * 2. After definition is completed any number of threads may proceed concurrently In order to achieve this container + * has three phases (init, defining, defined). At init phase (that is no definition has been set) any number of threads + * may proceed concurrently. When a thread sets a definition {@code setDefinition} container enters the defining phase. + * In that phase only that thread may continue in {@code getSemType} method (this is to allow for recursive type + * definitions). Container registers with the {@code Definition} using {@code registerContainer} method. When the + * {@code Definition} has been defined (ie. {@code Env} has an atom corresponding to the definition) it must notify the + * container using {@code definitionUpdated} method. At this point container moves to defined phase allowing concurrent + * access to {@code getSemType}. + * + * @param type of the definition + * @since 2201.11.0 + */ +public class DefinitionContainer { + + private final ReadWriteLock rwLock = new ReentrantReadWriteLock(); + private volatile E definition; + + private final ReentrantLock recTypeLock = new ReentrantLock(); + private volatile boolean isDefining = false; + + public boolean isDefinitionReady() { + try { + rwLock.readLock().lock(); + return definition != null; + } finally { + rwLock.readLock().unlock(); + } + } + + public SemType getSemType(Env env) { + try { + rwLock.readLock().lock(); + // We don't need this check to be synchronized since {@code trySetDefinition} will hold the write lock until + // it completes, So isDefining should always be at a consistent state + if (isDefining) { + // This should prevent threads other than the defining thread to access the rec atom. + recTypeLock.lock(); + } + return definition.getSemType(env); + } finally { + rwLock.readLock().unlock(); + } + } + + public DefinitionUpdateResult trySetDefinition(Supplier supplier) { + try { + rwLock.writeLock().lock(); + boolean updated; + E newDefinition; + if (this.definition != null) { + updated = false; + newDefinition = null; + } else { + updated = true; + newDefinition = supplier.get(); + newDefinition.registerContainer(this); + this.recTypeLock.lock(); + isDefining = true; + this.definition = newDefinition; + } + return new DefinitionUpdateResult<>(newDefinition, updated); + } finally { + rwLock.writeLock().unlock(); + } + } + + public void clear() { + try { + rwLock.writeLock().lock(); + // This shouldn't happen because defining thread should hold the lock. + assert !isDefining; + this.definition = null; + } finally { + rwLock.writeLock().unlock(); + } + } + + public void definitionUpdated() { + recTypeLock.unlock(); + isDefining = false; + } + + /** + * Result of trying to update the definition. + * + * @param Type of the definition + * @param updated If update was successful. If this failed you must get the semtype using the {@code getSemType} + * method of the container + * @param definition If update was successful this will be the new definition. Otherwise, this will be null + * @since 2201.11.0 + */ + public record DefinitionUpdateResult(E definition, boolean updated) { + + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/StringSubtype.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java similarity index 59% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/StringSubtype.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java index e2d25feb5261..6f798ca4d07c 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/StringSubtype.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/DelegatedSubType.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -15,15 +15,17 @@ * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype.subtypedata; -import io.ballerina.semtype.ProperSubtypeData; +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SubType; /** - * Represent StringSubtype. + * Represents the subtype implemented by BDDs. * - * @since 2.0.0 + * @since 2201.11.0 */ -public class StringSubtype implements ProperSubtypeData { +public interface DelegatedSubType extends SubTypeData { + SubType inner(); } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/EnumerableSubtypeData.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/EnumerableSubtypeData.java new file mode 100644 index 000000000000..4f8429d51f1f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/EnumerableSubtypeData.java @@ -0,0 +1,181 @@ +/* + * + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * / + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import java.util.Arrays; +import java.util.List; + +/** + * All {@code SubTypeData} where we can enumerate individual values must extend + * this class. It will provide common operations such as {@code union}, + * {@code intersect} and {@code diff} for all such data. + * + * @param type individual value in the subset + * @since 2201.11.0 + */ +public abstract class EnumerableSubtypeData> { + + public abstract boolean allowed(); + + public abstract E[] values(); + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof EnumerableSubtypeData other)) { + return false; + } + return other.allowed() == this.allowed() && Arrays.equals(other.values(), this.values()); + } + + @Override + public int hashCode() { + return super.hashCode(); + } + + boolean union(EnumerableSubtypeData other, List results) { + boolean b1 = this.allowed(); + boolean b2 = other.allowed(); + if (b1 && b2) { + enumerableListUnion(this.values(), other.values(), results); + return true; + } else if (!b1 && !b2) { + enumerableListIntersection(this.values(), other.values(), results); + return false; + } else if (b1 && !b2) { + enumerableListDiff(other.values(), this.values(), results); + return false; + } else { + enumerableListDiff(this.values(), other.values(), results); + return false; + } + } + + boolean intersect(EnumerableSubtypeData other, List results) { + boolean b1 = this.allowed(); + boolean b2 = other.allowed(); + if (b1 && b2) { + enumerableListIntersection(this.values(), other.values(), results); + return true; + } else if (!b1 && !b2) { + enumerableListUnion(this.values(), other.values(), results); + return false; + } else if (b1 && !b2) { + enumerableListDiff(this.values(), other.values(), results); + return true; + } else { + enumerableListDiff(other.values(), this.values(), results); + return true; + } + } + + private static > void enumerableListUnion(E[] values1, E[] values2, List results) { + int i1, i2; + i1 = i2 = 0; + int len1 = values1.length; + int len2 = values2.length; + while (true) { + if (i1 >= len1) { + if (i2 >= len2) { + break; + } + results.add(values2[i2]); + i2 += 1; + } else if (i2 >= len2) { + results.add(values1[i1]); + i1 += 1; + } else { + E s1 = values1[i1]; + E s2 = values2[i2]; + int result = s1.compareTo(s2); + if (result == 0) { + results.add(s1); + i1 += 1; + i2 += 1; + } else if (result < 0) { + results.add(s1); + i1 += 1; + } else { + results.add(s2); + i2 += 1; + } + } + } + } + + private static > void enumerableListIntersection(E[] v1, E[] v2, List results) { + int i1, i2; + i1 = i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + while (true) { + // TODO: refactor condition + if (i1 >= len1 || i2 >= len2) { + break; + } else { + E s1 = v1[i1]; + E s2 = v2[i2]; + int result = s1.compareTo(s2); + if (result == 0) { + results.add(s1); + i1 += 1; + i2 += 1; + } else if (result < 0) { + i1 += 1; + } else { + i2 += 1; + } + } + } + } + + private static > void enumerableListDiff(E[] t1, E[] t2, List results) { + int i1, i2; + i1 = i2 = 0; + int len1 = t1.length; + int len2 = t2.length; + while (true) { + if (i1 >= len1) { + break; + } + if (i2 >= len2) { + results.add(t1[i1]); + i1 += 1; + } else { + E s1 = t1[i1]; + E s2 = t2[i2]; + int result = s1.compareTo(s2); + if (result == 0) { + i1 += 1; + i2 += 1; + } else if (result < 0) { + results.add(s1); + i1 += 1; + } else { + i2 += 1; + } + } + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ErrorUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ErrorUtils.java new file mode 100644 index 000000000000..870fae6f4e7a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ErrorUtils.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_ERROR; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_MAPPING; +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Builder.basicSubType; +import static io.ballerina.runtime.api.types.semtype.RecAtom.createDistinctRecAtom; + +/** + * Utility methods for creating error types. + * + * @since 2201.11.0 + */ +public final class ErrorUtils { + + private ErrorUtils() { + } + + public static SemType errorDetail(SemType detail) { + SubTypeData data = Core.subTypeData(detail, BT_MAPPING); + if (data == AllOrNothing.ALL) { + return Builder.getErrorType(); + } else if (data == AllOrNothing.NOTHING) { + return Builder.getNeverType(); + } + + assert data instanceof Bdd; + SubType sd = ((Bdd) data).intersect(Builder.getBddSubtypeRo()); + if (sd.equals(Builder.getBddSubtypeRo())) { + return Builder.getErrorType(); + } + return basicSubType(BT_ERROR, BErrorSubType.createDelegate(sd)); + } + + public static SemType errorDistinct(int distinctId) { + assert distinctId >= 0; + Bdd bdd = bddAtom(createDistinctRecAtom(-distinctId - 1)); + return basicSubType(BT_ERROR, BErrorSubType.createDelegate(bdd)); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FixedLengthArray.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FixedLengthArray.java new file mode 100644 index 000000000000..8284a390a38b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FixedLengthArray.java @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Represents required member part of a list atom. + * + * @since 2201.11.0 + */ +public final class FixedLengthArray { + + // We have a separate fixedLength so types such as {@code byte[500]} don't need to store 500 elements + // in {@code initial}. + private final SemType[] initial; + private final int fixedLength; + private Integer hashCode; + + private FixedLengthArray(SemType[] initial, int fixedLength) { + for (SemType semType : initial) { + if (semType == null) { + throw new IllegalArgumentException("initial members can't be null"); + } + } + this.initial = initial; + this.fixedLength = fixedLength; + } + + public static FixedLengthArray from(SemType[] initial, int fixedLength) { + return new FixedLengthArray(initial, fixedLength); + } + + private static final FixedLengthArray EMPTY = new FixedLengthArray(new SemType[0], 0); + + static FixedLengthArray normalized(SemType[] initial, int fixedLength) { + if (initial.length < 2) { + return new FixedLengthArray(initial, fixedLength); + } + int i = initial.length - 1; + SemType last = initial[i]; + i -= 1; + while (i >= 0) { + if (last != initial[i]) { + break; + } + i -= 1; + } + int length = Integer.max(1, i + 2); + SemType[] buffer = new SemType[length]; + if (length == 1) { + buffer[0] = last; + } else { + System.arraycopy(initial, 0, buffer, 0, length); + } + return new FixedLengthArray(buffer, fixedLength); + } + + public static FixedLengthArray empty() { + return EMPTY; + } + + public FixedLengthArray shallowCopy() { + // TODO: may be create a copy of write array instead + return new FixedLengthArray(initial.clone(), fixedLength); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof FixedLengthArray that)) { + return false; + } + return fixedLength == that.fixedLength && Objects.deepEquals(initial, that.initial); + } + + @Override + public int hashCode() { + Integer result = hashCode; + if (result == null) { + synchronized (this) { + result = hashCode; + if (result == null) { + hashCode = result = computeHashCode(); + } + } + } + return result; + } + + private int computeHashCode() { + return Objects.hash(Arrays.hashCode(initial), fixedLength); + } + + public int fixedLength() { + return fixedLength; + } + + public SemType[] initial() { + return initial; + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionAtomicType.java new file mode 100644 index 000000000000..349ca151de59 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionAtomicType.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.AtomicType; +import io.ballerina.runtime.api.types.semtype.SemType; + +/** + * Represents a function atomic type. + * + * @param paramType function parameters. This is a list type + * @param retType return type + * @param qualifiers function qualifiers. This is a list type + * @since 2201.11.0 + */ +public record FunctionAtomicType(SemType paramType, SemType retType, SemType qualifiers) implements AtomicType { + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java new file mode 100644 index 000000000000..4878cbe1f717 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionDefinition.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.RecAtom; +import io.ballerina.runtime.api.types.semtype.SemType; + +/** + * {@code Definition} used to create function subtypes. + * + * @since 2201.11.0 + */ +public class FunctionDefinition extends Definition { + + private volatile RecAtom rec; + private volatile SemType semType; + + @Override + public SemType getSemType(Env env) { + if (this.semType != null) { + return this.semType; + } else { + RecAtom rec = env.recFunctionAtom(); + this.rec = rec; + return this.createSemType(rec); + } + } + + private SemType createSemType(Atom atom) { + BddNode bdd = BddNode.bddAtom(atom); + SemType semType = Builder.basicSubType(BasicTypeCode.BT_FUNCTION, BFunctionSubType.createDelegate(bdd)); + this.semType = semType; + return semType; + } + + public SemType define(Env env, SemType args, SemType ret, FunctionQualifiers qualifiers) { + FunctionAtomicType atomicType = new FunctionAtomicType(args, ret, qualifiers.toSemType(env)); + RecAtom rec = this.rec; + Atom atom; + if (rec != null) { + atom = rec; + env.setRecFunctionAtomType(rec, atomicType); + } else { + atom = env.functionAtom(atomicType); + } + SemType semType = this.createSemType(atom); + notifyContainer(); + return semType; + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionQualifiers.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionQualifiers.java new file mode 100644 index 000000000000..0b90e9bad41d --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FunctionQualifiers.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +/** + * Represents the qualifiers of a function. + * + * @since 2201.11.0 + */ +public final class FunctionQualifiers { + + private static final FunctionQualifiers DEFAULT = new FunctionQualifiers(false, false); + private final boolean isolated; + private final boolean transactional; + private SemType semType; + + private FunctionQualifiers(boolean isolated, boolean transactional) { + this.isolated = isolated; + this.transactional = transactional; + } + + public static FunctionQualifiers create(boolean isolated, boolean transactional) { + if (!isolated && !transactional) { + return DEFAULT; + } + return new FunctionQualifiers(isolated, transactional); + } + + synchronized SemType toSemType(Env env) { + if (semType == null) { + ListDefinition ld = new ListDefinition(); + SemType[] members = { + isolated ? Builder.getBooleanConst(true) : Builder.getBooleanType(), + transactional ? Builder.getBooleanType() : Builder.getBooleanConst(false) + }; + semType = ld.defineListTypeWrapped(env, members, 2, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_NONE); + } + return semType; + } + + public boolean isolated() { + return isolated; + } + + public boolean transactional() { + return transactional; + } + + @Override + public String toString() { + return "FunctionQualifiers[" + + "isolated=" + isolated + ", " + + "transactional=" + transactional + ']'; + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FutureUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FutureUtils.java new file mode 100644 index 000000000000..8c927ac0b442 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/FutureUtils.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.Core.createBasicSemType; +import static io.ballerina.runtime.api.types.semtype.Core.subTypeData; + +/** + * Utility methods for creating future types. + * + * @since 2201.11.0 + */ +public final class FutureUtils { + + private static final MappingDefinition.Field[] EMPTY_FIELDS = new MappingDefinition.Field[0]; + + private FutureUtils() { + } + + public static SemType futureContaining(Env env, SemType constraint) { + if (constraint == Builder.getValType()) { + return Builder.getFutureType(); + } + MappingDefinition md = new MappingDefinition(); + SemType mappingType = md.defineMappingTypeWrapped(env, EMPTY_FIELDS, constraint, + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + Bdd bdd = (Bdd) subTypeData(mappingType, BasicTypeCode.BT_MAPPING); + return createBasicSemType(BasicTypeCode.BT_FUTURE, bdd); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ImmutableSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ImmutableSemType.java new file mode 100644 index 000000000000..d63513b1b31f --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ImmutableSemType.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; +import io.ballerina.runtime.internal.types.BSemTypeWrapper; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Runtime representation of an immutable semtype. + * + * @since 2201.11.0 + */ +public abstract sealed class ImmutableSemType extends SemType permits BSemTypeWrapper { + + private static final SubType[] EMPTY_SUBTYPE_DATA = new SubType[0]; + + private Integer hashCode; + + ImmutableSemType(int all, int some, SubType[] subTypeData) { + super(all, some, subTypeData); + } + + ImmutableSemType(int all) { + this(all, 0, EMPTY_SUBTYPE_DATA); + } + + protected ImmutableSemType(SemType semType) { + this(semType.all(), semType.some(), semType.subTypeData()); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ImmutableSemType semType)) { + return false; + } + return all() == semType.all() && some() == semType.some() && + Objects.deepEquals(this.subTypeData(), semType.subTypeData()); + } + + @Override + public int hashCode() { + Integer result = hashCode; + if (result == null) { + synchronized (this) { + result = hashCode; + if (result == null) { + hashCode = result = computeHashCode(); + } + } + } + return result; + } + + private int computeHashCode() { + return Objects.hash(all(), some(), Arrays.hashCode(subTypeData())); + } + + @Override + protected void setAll(int all) { + throw new UnsupportedOperationException("Immutable semtypes cannot be modified"); + } + + @Override + protected void setSome(int some, SubType[] subTypeData) { + throw new UnsupportedOperationException("Immutable semtypes cannot be modified"); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListAtomicType.java new file mode 100644 index 000000000000..5631bbc55036 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListAtomicType.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.AtomicType; +import io.ballerina.runtime.api.types.semtype.SemType; + +/** + * Represent list atomic type. + * + * @param members required member types of the list + * @param rest rest of member type of the list + * @since 2201.11.0 + */ +public record ListAtomicType(FixedLengthArray members, SemType rest) implements AtomicType { + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java new file mode 100644 index 000000000000..58bd4674453e --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ListDefinition.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.RecAtom; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Builder.basicSubType; +import static io.ballerina.runtime.api.types.semtype.Builder.getUndefType; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; +import static io.ballerina.runtime.api.types.semtype.Core.union; +import static io.ballerina.runtime.internal.types.semtype.CellAtomicType.CellMutability.CELL_MUT_NONE; + +/** + * {@code Definition} used to create a list type. + * + * @since 2201.11.0 + */ +public class ListDefinition extends Definition { + + private volatile RecAtom rec = null; + private volatile SemType semType = null; + + @Override + public SemType getSemType(Env env) { + SemType s = this.semType; + if (s == null) { + RecAtom rec = env.recListAtom(); + this.rec = rec; + return this.createSemType(env, rec); + } + return s; + } + + public SemType defineListTypeWrapped(Env env, SemType[] initial, int fixedLength, SemType rest, + CellAtomicType.CellMutability mut) { + SemType[] initialCells = new SemType[initial.length]; + for (int i = 0; i < initial.length; i++) { + initialCells[i] = Builder.getCellContaining(env, initial[i], mut); + } + SemType restCell = + Builder.getCellContaining(env, union(rest, getUndefType()), isNever(rest) ? CELL_MUT_NONE : mut); + SemType semType = define(env, initialCells, fixedLength, restCell); + notifyContainer(); + return semType; + } + + private SemType define(Env env, SemType[] initial, int fixedLength, SemType rest) { + FixedLengthArray members = FixedLengthArray.normalized(initial, fixedLength); + ListAtomicType atomicType = new ListAtomicType(members, rest); + Atom atom; + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecListAtomType(rec, atomicType); + } else { + atom = env.listAtom(atomicType); + } + return this.createSemType(env, atom); + } + + private SemType createSemType(Env env, Atom atom) { + BddNode bdd = bddAtom(atom); + this.semType = basicSubType(BasicTypeCode.BT_LIST, BListSubType.createDelegate(bdd)); + return this.semType; + } + +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingAtomicType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingAtomicType.java new file mode 100644 index 000000000000..9ad1916a6637 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingAtomicType.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.AtomicType; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.FieldPair; +import io.ballerina.runtime.api.types.semtype.FieldPairs; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.ArrayList; +import java.util.Collection; + +import static io.ballerina.runtime.api.types.semtype.Core.cellInner; +import static io.ballerina.runtime.api.types.semtype.Core.intersectCellMemberSemTypes; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; + +/** + * Represent mapping atomic type. + * + * @param names required member names of the mapping + * @param types required member types of the mapping + * @param rest rest of member type of the mapping + * @since 2201.11.0 + */ +public record MappingAtomicType(String[] names, SemType[] types, SemType rest) implements AtomicType { + + public MappingAtomicType { + assert names.length == types.length; + } + + public MappingAtomicType intersectMapping(Env env, MappingAtomicType other) { + int expectedSize = Integer.min(types().length, other.types().length); + Collection names = new ArrayList<>(expectedSize); + Collection types = new ArrayList<>(expectedSize); + for (FieldPair fieldPair : new FieldPairs(this, other)) { + names.add(fieldPair.name()); + SemType t = intersectCellMemberSemTypes(env, fieldPair.type1(), fieldPair.type2()); + if (isNever(cellInner(fieldPair.type1()))) { + return null; + + } + types.add(t); + } + SemType rest = intersectCellMemberSemTypes(env, this.rest(), other.rest()); + return new MappingAtomicType(names.toArray(String[]::new), types.toArray(SemType[]::new), rest); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java new file mode 100644 index 000000000000..5e9ae332e55b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MappingDefinition.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Atom; +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.RecAtom; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Arrays; +import java.util.Comparator; + +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Builder.basicSubType; +import static io.ballerina.runtime.api.types.semtype.Builder.getUndefType; +import static io.ballerina.runtime.api.types.semtype.Core.isNever; +import static io.ballerina.runtime.api.types.semtype.Core.union; + +/** + * {@code Definition} used to create a mapping type. + * + * @since 2201.11.0 + */ +public class MappingDefinition extends Definition { + + private volatile RecAtom rec = null; + private volatile SemType semType = null; + + @Override + public SemType getSemType(Env env) { + SemType s = this.semType; + if (s == null) { + RecAtom rec = env.recMappingAtom(); + this.rec = rec; + return this.createSemType(env, rec); + } else { + return s; + } + } + + private SemType createSemType(Env env, Atom atom) { + BddNode bdd = bddAtom(atom); + this.semType = basicSubType(BasicTypeCode.BT_MAPPING, BMappingSubType.createDelegate(bdd)); + return this.semType; + } + + public SemType defineMappingTypeWrapped(Env env, Field[] fields, SemType rest, CellAtomicType.CellMutability mut) { + assert rest != null; + BCellField[] cellFields = new BCellField[fields.length]; + for (int i = 0; i < fields.length; i++) { + Field field = fields[i]; + BCellField cellField = BCellField.from(env, field, mut); + cellFields[i] = cellField; + } + SemType restCell = Builder.getCellContaining(env, union(rest, getUndefType()), + isNever(rest) ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); + SemType semType = define(env, cellFields, restCell); + notifyContainer(); + return semType; + } + + SemType define(Env env, BCellField[] cellFields, SemType rest) { + String[] names = new String[cellFields.length]; + SemType[] types = new SemType[cellFields.length]; + sortAndSplitFields(cellFields, names, types); + MappingAtomicType atomicType = new MappingAtomicType(names, types, rest); + Atom atom; + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecMappingAtomType(rec, atomicType); + } else { + atom = env.mappingAtom(atomicType); + } + return this.createSemType(env, atom); + } + + private void sortAndSplitFields(BCellField[] fields, String[] names, SemType[] types) { + assert fields.length == names.length && fields.length == types.length; + Arrays.sort(fields, Comparator.comparing((field) -> field.name)); + for (int i = 0; i < fields.length; i++) { + names[i] = fields[i].name; + types[i] = fields[i].type; + } + } + + public record Field(String name, SemType ty, boolean readonly, boolean optional) { + + } + + record BCellField(String name, SemType type) { + + static BCellField from(Env env, Field field, CellAtomicType.CellMutability mut) { + SemType type = field.ty; + SemType cellType = Builder.getCellContaining(env, field.optional ? union(type, getUndefType()) : type, + field.readonly ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut); + BCellField cellField = new BCellField(field.name, cellType); + return cellField; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Member.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Member.java new file mode 100644 index 000000000000..19a6938b9d68 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/Member.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.Builder.getStringConst; + +/** + * Represents a member of an object type. + * + * @param name the name of the member. For methods, this is the method name, and for fields, this is the field + * name. + * @param valueTy the type of the member + * @param kind the kind of the member (either {@link Kind#Field} or {@link Kind#Method}) + * @param visibility the visibility of the member (either {@link Visibility#Public} or {@link Visibility#Private}) + * @param immutable whether the member is immutable + * @since 2201.11.0 + */ +public record Member(String name, SemType valueTy, Kind kind, Visibility visibility, boolean immutable) { + + public enum Kind { + Field, + Method; + + private static final MappingDefinition.Field FIELD = + new MappingDefinition.Field("kind", getStringConst("field"), true, false); + private static final MappingDefinition.Field METHOD = + new MappingDefinition.Field("kind", getStringConst("method"), true, false); + + public MappingDefinition.Field field() { + return switch (this) { + case Field -> FIELD; + case Method -> METHOD; + }; + } + } + + public enum Visibility { + Public, + Private; + + private static final SemType PUBLIC_TAG = getStringConst("public"); + private static final MappingDefinition.Field PUBLIC = + new MappingDefinition.Field("visibility", PUBLIC_TAG, true, false); + private static final SemType PRIVATE_TAG = getStringConst("private"); + private static final MappingDefinition.Field PRIVATE = + new MappingDefinition.Field("visibility", PRIVATE_TAG, true, false); + static final MappingDefinition.Field ALL = + new MappingDefinition.Field("visibility", Core.union(PRIVATE_TAG, PUBLIC_TAG), true, false); + + public MappingDefinition.Field field() { + return switch (this) { + case Public -> PUBLIC; + case Private -> PRIVATE; + }; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java new file mode 100644 index 000000000000..2059cb237766 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/MutableSemType.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.BType; + +/** + * Represents a mutable semantic type. Note in the current implementation we assume after type checking this is to be + * immutable. + * + * @since 2201.11.0 + */ +public sealed interface MutableSemType permits BType { + + SemType createSemType(); + + void resetSemType(); + + void updateInnerSemTypeIfNeeded(); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java new file mode 100644 index 000000000000..f843d04d43b2 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectDefinition.java @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddNode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.List; +import java.util.stream.Stream; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.BT_OBJECT; +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; +import static io.ballerina.runtime.api.types.semtype.Core.createBasicSemType; +import static io.ballerina.runtime.api.types.semtype.Core.union; +import static io.ballerina.runtime.api.types.semtype.RecAtom.createDistinctRecAtom; + +/** + * {@code Definition} used to create an object type. + *

+ * Each object type is represented as mapping type (with its basic type set to object) as fallows + * {@code { "$qualifiers": { boolean isolated, "client"|"service" network }, [field_name]: { "field"|"method" kind, + * "public"|"private" visibility, VAL value; } ...{ "field" kind, "public"|"private" visibility, VAL value; } | { + * "method" kind, "public"|"private" visibility, FUNCTION value; } }} + * + * @since 2201.11.0 + */ +public class ObjectDefinition extends Definition { + + private final MappingDefinition mappingDefinition = new MappingDefinition(); + + @Override + public SemType getSemType(Env env) { + return objectContaining(mappingDefinition.getSemType(env)); + } + + public SemType define(Env env, ObjectQualifiers qualifiers, List members, + CellAtomicType.CellMutability mut) { + Stream memberStream = + members.stream().map(member -> memberField(env, member, qualifiers.readonly())); + Stream qualifierStream = Stream.of(qualifiers.field(env)); + SemType mappingType = mappingDefinition.define(env, Stream.concat(memberStream, qualifierStream) + .map(field -> MappingDefinition.BCellField.from(env, field, mut)) + .toArray(MappingDefinition.BCellField[]::new), restMemberType(env, mut, qualifiers.readonly())); + SemType semType = objectContaining(mappingType); + notifyContainer(); + return semType; + } + + private SemType objectContaining(SemType mappingType) { + Bdd mappingSubTypeData = (Bdd) Core.subTypeData(mappingType, BasicTypeCode.BT_MAPPING); + return createBasicSemType(BT_OBJECT, mappingSubTypeData); + } + + private SemType restMemberType(Env env, CellAtomicType.CellMutability mut, boolean readonly) { + MappingDefinition fieldDefn = new MappingDefinition(); + SemType fieldMemberType = fieldDefn.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{ + new MappingDefinition.Field( + "value", + readonly ? Builder.getReadonlyType() : Builder.getValType(), + readonly, + false), + Member.Kind.Field.field(), Member.Visibility.ALL}, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + MappingDefinition methodDefn = new MappingDefinition(); + + SemType methodMemberType = methodDefn.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{ + new MappingDefinition.Field("value", Builder.getFunctionType(), true, false), + Member.Kind.Method.field(), Member.Visibility.ALL}, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + return Builder.getCellContaining(env, union(fieldMemberType, methodMemberType), mut); + } + + private static MappingDefinition.Field memberField(Env env, Member member, boolean immutableObject) { + MappingDefinition md = new MappingDefinition(); + SemType semtype = md.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{ + new MappingDefinition.Field("value", member.valueTy(), member.immutable(), false), + member.kind().field(), member.visibility().field()}, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + return new MappingDefinition.Field(member.name(), semtype, immutableObject | member.immutable(), false); + } + + public static SemType distinct(int distinctId) { + assert distinctId >= 0; + BddNode bdd = bddAtom(createDistinctRecAtom(-distinctId - 1)); + return createBasicSemType(BT_OBJECT, bdd); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectQualifiers.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectQualifiers.java new file mode 100644 index 000000000000..33d52a1816a9 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/ObjectQualifiers.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.Builder.getBooleanConst; +import static io.ballerina.runtime.api.types.semtype.Builder.getStringConst; +import static io.ballerina.runtime.api.types.semtype.Core.union; + +/** + * Represents the qualifiers of an object type. + * + * @param isolated whether the object is isolated + * @param readonly whether the object is readonly + * @param networkQualifier the network qualifier of the object (either {@link NetworkQualifier#Client}, + * {@link NetworkQualifier#Service}, or {@link NetworkQualifier#None}) + * @since 2201.11.0 + */ +public record ObjectQualifiers(boolean isolated, boolean readonly, NetworkQualifier networkQualifier) { + + public MappingDefinition.Field field(Env env) { + MappingDefinition md = new MappingDefinition(); + MappingDefinition.Field isolatedField = + new MappingDefinition.Field("isolated", isolated ? getBooleanConst(true) : Builder.getBooleanType(), + true, false); + MappingDefinition.Field networkField = networkQualifier.field(); + SemType ty = md.defineMappingTypeWrapped(env, new MappingDefinition.Field[]{isolatedField, networkField}, + Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_NONE); + return new MappingDefinition.Field("$qualifiers", ty, true, false); + } + + public enum NetworkQualifier { + Client, + Service, + None; + + private static final SemType CLIENT_TAG = getStringConst("client"); + private static final MappingDefinition.Field CLIENT = + new MappingDefinition.Field("network", CLIENT_TAG, true, false); + + private static final SemType SERVICE_TAG = getStringConst("service"); + private static final MappingDefinition.Field SERVICE = + new MappingDefinition.Field("network", SERVICE_TAG, true, false); + + // Object can't be both client and service, which is enforced by the enum. We are using a union here so that + // if this is none it matches both + private static final MappingDefinition.Field NONE = + new MappingDefinition.Field("network", union(CLIENT_TAG, SERVICE_TAG), true, false); + + private MappingDefinition.Field field() { + return switch (this) { + case Client -> CLIENT; + case Service -> SERVICE; + case None -> NONE; + }; + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/RegexUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/RegexUtils.java new file mode 100644 index 000000000000..39c4264ba8cc --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/RegexUtils.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.SemType; + +/** + * Utility class for creating semtypes of regex tagged basic type. + * + * @since 2201.11.0 + */ +public final class RegexUtils { + + private RegexUtils() { + + } + + public static SemType regexShape(String value) { + SemType stringSubtype = Builder.getStringConst(value); + BStringSubType stringSubType = (BStringSubType) stringSubtype.subTypeByCode(BasicTypeCode.CODE_STRING); + return Builder.basicSubType(BasicTypeCode.BT_REGEXP, stringSubType); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeHelper.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeHelper.java new file mode 100644 index 000000000000..7bad30e0127b --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SemTypeHelper.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_BOOLEAN; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_CELL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_DECIMAL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_ERROR; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_FLOAT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_FUNCTION; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_FUTURE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_HANDLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_INT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_LIST; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_MAPPING; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_NIL; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_OBJECT; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_REGEXP; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_STREAM; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_STRING; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_TABLE; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_TYPEDESC; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_UNDEF; +import static io.ballerina.runtime.api.types.semtype.BasicTypeCode.CODE_XML; + +/** + * Collection of utility function on {@code SemType}. + * + * @since 2201.11.0 + */ +public final class SemTypeHelper { + + private SemTypeHelper() { + } + + public static String stringRepr(SemType ty) { + return "all[" + bitSetRepr(ty.all()) + "] some [" + bitSetRepr(ty.some()) + "]"; + } + + public static String bitSetRepr(int bits) { + StringBuilder sb = new StringBuilder(); + appendBitSetRepr(sb, bits, CODE_NIL, "NIL"); + appendBitSetRepr(sb, bits, CODE_BOOLEAN, "BOOLEAN"); + appendBitSetRepr(sb, bits, CODE_INT, "INT"); + appendBitSetRepr(sb, bits, CODE_FLOAT, "FLOAT"); + appendBitSetRepr(sb, bits, CODE_DECIMAL, "DECIMAL"); + appendBitSetRepr(sb, bits, CODE_STRING, "STRING"); + appendBitSetRepr(sb, bits, CODE_ERROR, "ERROR"); + appendBitSetRepr(sb, bits, CODE_TYPEDESC, "TYPE_DESC"); + appendBitSetRepr(sb, bits, CODE_HANDLE, "HANDLE"); + appendBitSetRepr(sb, bits, CODE_REGEXP, "REGEXP"); + appendBitSetRepr(sb, bits, CODE_FUNCTION, "FUNCTION"); + appendBitSetRepr(sb, bits, CODE_FUTURE, "FUTURE"); + appendBitSetRepr(sb, bits, CODE_STREAM, "STREAM"); + appendBitSetRepr(sb, bits, CODE_LIST, "LIST"); + appendBitSetRepr(sb, bits, CODE_MAPPING, "MAPPING"); + appendBitSetRepr(sb, bits, CODE_TABLE, "TABLE"); + appendBitSetRepr(sb, bits, CODE_XML, "XML"); + appendBitSetRepr(sb, bits, CODE_OBJECT, "OBJECT"); + appendBitSetRepr(sb, bits, CODE_CELL, "CELL"); + appendBitSetRepr(sb, bits, CODE_UNDEF, "UNDEF"); + return sb.toString(); + } + + private static void appendBitSetRepr(StringBuilder sb, int bits, int index, String name) { + int mask = 1 << index; + if ((bits & mask) != 0) { + if (!sb.isEmpty()) { + sb.append(", "); + } + sb.append(name).append(" "); + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java new file mode 100644 index 000000000000..2023672f5848 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/StreamDefinition.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.Core.createBasicSemType; +import static io.ballerina.runtime.api.types.semtype.Core.subTypeData; + +/** + * {@code Definition} used to create a stream type. Stream is represented as a + * tuple of {@code [valueType, completionType]} + * + * @since 2201.11.0 + */ +public class StreamDefinition extends Definition { + + private final ListDefinition listDefinition = new ListDefinition(); + + @Override + public SemType getSemType(Env env) { + return streamContaining(listDefinition.getSemType(env)); + } + + public SemType define(Env env, SemType valueType, SemType completionType) { + if (Builder.getValType() == completionType && Builder.getValType() == valueType) { + return Builder.getStreamType(); + } + SemType tuple = listDefinition.defineListTypeWrapped(env, new SemType[]{valueType, completionType}, 2, + Builder.getNeverType(), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + SemType semType = streamContaining(tuple); + notifyContainer(); + return semType; + } + + private SemType streamContaining(SemType tupleType) { + SubTypeData bdd = subTypeData(tupleType, BasicTypeCode.BT_LIST); + assert bdd instanceof Bdd; + return createBasicSemType(BasicTypeCode.BT_STREAM, (Bdd) bdd); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/Atom.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java similarity index 67% rename from semtypes/src/main/java/io/ballerina/semtype/Atom.java rename to bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java index 1e9461723580..a418583d10af 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/Atom.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubTypeData.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -15,12 +15,14 @@ * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; + +package io.ballerina.runtime.internal.types.semtype; /** - * Represent the BDD atom. + * Marker interface for SubTypeData. * - * @since 2.0.0 + * @since 2201.11.0 */ -public interface Atom { +public interface SubTypeData { + } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePair.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePair.java new file mode 100644 index 000000000000..d56e4bd33899 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePair.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SubType; + +/** + * Represents the corresponding subtype pairs of two semtypes. + * + * @param typeCode the type code of the semtype + * @param subType1 the first subtype. This will if the first semtype don't have + * this subtype + * @param subType2 the second subtype. This will if the second semtype don't + * have this subtype + * @since 2201.11.0 + */ +public record SubtypePair(int typeCode, SubType subType1, SubType subType2) { + + public SubtypePair { + if (subType1 == null && subType2 == null) { + throw new IllegalArgumentException("both subType1 and subType2 cannot be null"); + } + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairIterator.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairIterator.java new file mode 100644 index 000000000000..f061b012896a --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairIterator.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Iterator; + +/** + * Iteration implementation of `SubtypePairIterator`. + * + * @since 2201.11.0 + */ +final class SubtypePairIterator implements Iterator { + + // NOTE: this needs to be very efficient since pretty much all type operations depends on it + private int index = 0; + private static final int maxIndex = BasicTypeCode.CODE_UNDEF + 1; + private final int some; + private final SemType t1; + private final SemType t2; + + SubtypePairIterator(SemType t1, SemType t2, int some) { + this.some = some; + this.t1 = t1; + this.t2 = t2; + incrementIndex(); + } + + @Override + public boolean hasNext() { + return index < maxIndex; + } + + private void incrementIndex() { + int rest = some >> index; + int offset = Integer.numberOfTrailingZeros(rest); + index += offset; + } + + @Override + public SubtypePair next() { + SubType subType1 = t1.subTypeByCode(index); + SubType subType2 = t2.subTypeByCode(index); + assert (!(subType1 == null && subType2 == null)); + int typeCode = index; + index++; + incrementIndex(); + return new SubtypePair(typeCode, subType1, subType2); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairs.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairs.java new file mode 100644 index 000000000000..95844611c535 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/SubtypePairs.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Iterator; + +/** + * Implements the iterable for `SubtypePairIteratorImpl`. + * + * @since 2201.11.0 + */ +public class SubtypePairs implements Iterable { + + private final SemType t1; + private final SemType t2; + private final int bits; + + public SubtypePairs(SemType t1, SemType t2, int bits) { + this.t1 = t1; + this.t2 = t2; + this.bits = bits; + } + + @Override + public Iterator iterator() { + return new SubtypePairIterator(t1, t2, bits); + } +} 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 new file mode 100644 index 000000000000..158259252366 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TableUtils.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +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.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.Optional; + +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; + +/** + * Utility class for creating semtypes of table type. + * + * @since 2201.11.0 + */ +public final class TableUtils { + + private static final SemType[] EMPTY_SEMTYPE_ARR = new SemType[0]; + + 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++) { + SemType key = Builder.getStringConst(fieldNames[i]); + fieldNameSingletons[i] = key; + fieldTypes[i] = Core.mappingMemberTypeInnerVal(cx, tableConstraint, key); + } + + SemType normalizedKs = + new ListDefinition().defineListTypeWrapped(cx.env, fieldNameSingletons, fieldNameSingletons.length, + Builder.getNeverType(), CELL_MUT_NONE); + + SemType normalizedKc = fieldNames.length > 1 ? new ListDefinition().defineListTypeWrapped(cx.env, fieldTypes, + fieldTypes.length, Builder.getNeverType(), CELL_MUT_NONE) : fieldTypes[0]; + + 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(); + return switch (member.fixedLength()) { + case 0 -> Builder.getValType(); + case 1 -> Core.cellAtomicType(member.initial()[0]).orElseThrow().ty(); + default -> keyConstraint; + }; + }).orElse(keyConstraint); + 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); + } + + private static SemType tableContaining(Env env, SemType tableConstraint, SemType normalizedKc, SemType normalizedKs, + CellAtomicType.CellMutability mut) { + tableConstraint = Core.intersect(tableConstraint, Builder.getMappingType()); + ListDefinition typeParamArrDef = new ListDefinition(); + SemType typeParamArray = typeParamArrDef.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, tableConstraint, mut); + + ListDefinition listDef = new ListDefinition(); + SemType tupleType = + listDef.defineListTypeWrapped(env, new SemType[]{typeParamArray, normalizedKc, normalizedKs}, 3, + Builder.getNeverType(), + CELL_MUT_LIMITED); + Bdd bdd = (Bdd) Core.subTypeData(tupleType, BasicTypeCode.BT_LIST); + return Core.createBasicSemType(BasicTypeCode.BT_TABLE, bdd); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypedescUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypedescUtils.java new file mode 100644 index 000000000000..4bf7b6a78406 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/TypedescUtils.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import static io.ballerina.runtime.api.types.semtype.Core.createBasicSemType; +import static io.ballerina.runtime.api.types.semtype.Core.subTypeData; + +/** + * Utility class for creating semtypes of typedesc type. + * + * @since 2201.11.0 + */ +public final class TypedescUtils { + + private static final MappingDefinition.Field[] EMPTY_FIELDS = new MappingDefinition.Field[0]; + + private TypedescUtils() { + + } + + public static SemType typedescContaining(Env env, SemType constraint) { + if (constraint == Builder.getValType()) { + return Builder.getTypeDescType(); + } + MappingDefinition md = new MappingDefinition(); + SemType mappingType = md.defineMappingTypeWrapped(env, EMPTY_FIELDS, constraint, + CellAtomicType.CellMutability.CELL_MUT_NONE); + Bdd bdd = (Bdd) subTypeData(mappingType, BasicTypeCode.BT_MAPPING); + return createBasicSemType(BasicTypeCode.BT_TYPEDESC, bdd); + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/XmlUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/XmlUtils.java new file mode 100644 index 000000000000..f05813ead001 --- /dev/null +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/types/semtype/XmlUtils.java @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.internal.types.semtype; + +import io.ballerina.runtime.api.types.semtype.BasicTypeCode; +import io.ballerina.runtime.api.types.semtype.Bdd; +import io.ballerina.runtime.api.types.semtype.BddAllOrNothing; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.RecAtom; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.SubType; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import static io.ballerina.runtime.api.types.semtype.BddNode.bddAtom; + +/** + * Utility class for creating semtypes of XML type. + * + * @since 2201.11.0 + */ +public final class XmlUtils { + + public static final int XML_PRIMITIVE_NEVER = 1; + public static final int XML_PRIMITIVE_TEXT = 1 << 1; + public static final int XML_PRIMITIVE_ELEMENT_RO = 1 << 2; + public static final int XML_PRIMITIVE_PI_RO = 1 << 3; + public static final int XML_PRIMITIVE_COMMENT_RO = 1 << 4; + public static final int XML_PRIMITIVE_ELEMENT_RW = 1 << 5; + public static final int XML_PRIMITIVE_PI_RW = 1 << 6; + public static final int XML_PRIMITIVE_COMMENT_RW = 1 << 7; + + public static final int XML_PRIMITIVE_RO_SINGLETON = XML_PRIMITIVE_TEXT | XML_PRIMITIVE_ELEMENT_RO + | XML_PRIMITIVE_PI_RO | XML_PRIMITIVE_COMMENT_RO; + public static final int XML_PRIMITIVE_RO_MASK = XML_PRIMITIVE_NEVER | XML_PRIMITIVE_RO_SINGLETON; + public static final int XML_PRIMITIVE_RW_MASK = XML_PRIMITIVE_ELEMENT_RW | XML_PRIMITIVE_PI_RW + | XML_PRIMITIVE_COMMENT_RW; + public static final int XML_PRIMITIVE_SINGLETON = XML_PRIMITIVE_RO_SINGLETON | XML_PRIMITIVE_RW_MASK; + public static final int XML_PRIMITIVE_ALL_MASK = XML_PRIMITIVE_RO_MASK | XML_PRIMITIVE_RW_MASK; + + public static final SubTypeData XML_SUBTYPE_TOP = from(XML_PRIMITIVE_ALL_MASK, BddAllOrNothing.ALL); + public static final SubType XML_SUBTYPE_RO = + BXmlSubType.createDelegate(XML_PRIMITIVE_RO_MASK, + bddAtom(RecAtom.createRecAtom(XML_PRIMITIVE_RO_SINGLETON))); + + private XmlUtils() { + } + + public static SemType xmlSingleton(int primitive) { + if (XmlSingletonCache.isCached(primitive)) { + return XmlSingletonCache.get(primitive); + } + return createXmlSingleton(primitive); + } + + private static SemType createXmlSingleton(int primitive) { + return createXmlSemtype(createXmlSubtype(primitive, BddAllOrNothing.NOTHING)); + } + + private static SemType createXmlSemtype(SubTypeData xmlSubtype) { + if (xmlSubtype instanceof AllOrNothing) { + return xmlSubtype == AllOrNothing.ALL ? Builder.getXmlType() : Builder.getNeverType(); + } + assert xmlSubtype instanceof BXmlSubType : "subtype must be wrapped by delegate by now"; + return Builder.basicSubType(BasicTypeCode.BT_XML, (SubType) xmlSubtype); + } + + private static SubTypeData createXmlSubtype(int primitives, Bdd sequence) { + int p = primitives & XML_PRIMITIVE_ALL_MASK; + if (primitiveShouldIncludeNever(p)) { + p |= XML_PRIMITIVE_NEVER; + } + if (sequence == BddAllOrNothing.ALL && p == XML_PRIMITIVE_ALL_MASK) { + return AllOrNothing.ALL; + } else if (sequence == BddAllOrNothing.NOTHING && p == 0) { + return AllOrNothing.NOTHING; + } + return from(p, sequence); + } + + private static boolean primitiveShouldIncludeNever(int primitive) { + return (primitive & XML_PRIMITIVE_TEXT) == XML_PRIMITIVE_TEXT; + } + + public static SubTypeData from(int primitives, Bdd sequence) { + return BXmlSubType.createDelegate(primitives, sequence); + } + + public static SemType xmlSequence(SemType constituentType) { + assert Core.isSubtypeSimple(constituentType, Builder.getXmlType()) : + "It is a precondition that constituentType is a subtype of XML"; + if (Core.isNever(constituentType)) { + return xmlSequence(xmlSingleton(XML_PRIMITIVE_NEVER)); + } else if (constituentType.some() == 0) { + assert Core.isNever(Core.diff(Builder.getXmlType(), constituentType)); + return constituentType; + } else { + SubType xmlSubType = + Core.getComplexSubtypeData(constituentType, BasicTypeCode.BT_XML); + if (!xmlSubType.isAll() && !xmlSubType.isNothing()) { + xmlSubType = makeXmlSequence((BXmlSubType) xmlSubType); + } + return createXmlSemtype((SubTypeData) xmlSubType); + } + } + + private static SubType makeXmlSequence(BXmlSubType xmlSubType) { + int primitives = xmlSubType.primitives() | XML_PRIMITIVE_NEVER; + int atom = xmlSubType.primitives() & XML_PRIMITIVE_SINGLETON; + Bdd sequence = (Bdd) xmlSubType.bdd().union(bddAtom(RecAtom.createRecAtom(atom))); + return BXmlSubType.createDelegate(primitives, sequence); + } + + private static final class XmlSingletonCache { + + private static final Map CACHE = new ConcurrentHashMap<>(); + + private static boolean isCached(int primitive) { + return Integer.bitCount(primitive) < 3; + } + + private static SemType get(int primitive) { + return CACHE.computeIfAbsent(primitive, XmlUtils::createXmlSingleton); + } + + } +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java index 2ae27c4cd2cb..62ac70673626 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/MapUtils.java @@ -20,22 +20,22 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Core; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.TypeChecker; import io.ballerina.runtime.internal.errors.ErrorCodes; import io.ballerina.runtime.internal.errors.ErrorHelper; -import io.ballerina.runtime.internal.types.BMapType; import io.ballerina.runtime.internal.types.BRecordType; import io.ballerina.runtime.internal.types.BTypeReferenceType; -import io.ballerina.runtime.internal.types.BUnionType; import io.ballerina.runtime.internal.values.MapValue; -import java.util.List; - import static io.ballerina.runtime.api.constants.RuntimeConstants.MAP_LANG_LIB; import static io.ballerina.runtime.internal.errors.ErrorReasons.INHERENT_TYPE_VIOLATION_ERROR_IDENTIFIER; import static io.ballerina.runtime.internal.errors.ErrorReasons.MAP_KEY_NOT_FOUND_ERROR; @@ -56,7 +56,7 @@ public static void handleMapStore(MapValue mapValue, BString fi updateMapValue(TypeUtils.getImpliedType(mapValue.getType()), mapValue, fieldName, value); } - public static void handleInherentTypeViolatingMapUpdate(Object value, BMapType mapType) { + public static void handleInherentTypeViolatingMapUpdate(Object value, MapType mapType) { if (TypeChecker.checkIsType(value, mapType.getConstrainedType())) { return; } @@ -118,17 +118,7 @@ public static boolean handleInherentTypeViolatingRecordUpdate( } private static boolean containsNilType(Type type) { - type = TypeUtils.getImpliedType(type); - int tag = type.getTag(); - if (tag == TypeTags.UNION_TAG) { - List memTypes = ((BUnionType) type).getMemberTypes(); - for (Type memType : memTypes) { - if (containsNilType(memType)) { - return true; - } - } - } - return tag == TypeTags.NULL_TAG; + return Core.containsBasicType(SemType.tryInto(type), Builder.getNilType()); } public static BError createOpNotSupportedError(Type type, String op) { @@ -152,7 +142,7 @@ private static void updateMapValue(Type mapType, MapValue mapVa switch (mapType.getTag()) { case TypeTags.MAP_TAG: - handleInherentTypeViolatingMapUpdate(value, (BMapType) mapType); + handleInherentTypeViolatingMapUpdate(value, (MapType) mapType); mapValue.put(fieldName, value); return; case TypeTags.RECORD_TYPE_TAG: diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueComparisonUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueComparisonUtils.java index 48f33fda34ab..8bb1312eb5d2 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueComparisonUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueComparisonUtils.java @@ -219,14 +219,6 @@ public static int compareValues(Object lhsValue, Object rhsValue, String directi return codePointCompare(lhsValue.toString(), rhsValue.toString()); } - if (TypeTags.isIntegerTypeTag(lhsTypeTag) && TypeTags.isIntegerTypeTag(rhsTypeTag)) { - return Long.compare((long) lhsValue, (long) rhsValue); - } else if (TypeTags.isIntegerTypeTag(lhsTypeTag) && TypeTags.BYTE_TAG == rhsTypeTag) { - return Long.compare((long) lhsValue, (int) rhsValue); - } else if (TypeTags.BYTE_TAG == lhsTypeTag && TypeTags.isIntegerTypeTag(rhsTypeTag)) { - return Long.compare((int) lhsValue, (long) rhsValue); - } - if (lhsTypeTag == rhsTypeTag) { switch (lhsTypeTag) { case TypeTags.BOOLEAN_TAG: @@ -246,6 +238,14 @@ public static int compareValues(Object lhsValue, Object rhsValue, String directi } } + if (TypeTags.BYTE_TAG == lhsTypeTag && TypeTags.isIntegerTypeTag(rhsTypeTag)) { + return Long.compare((int) lhsValue, (long) rhsValue); + } else if (TypeTags.isIntegerTypeTag(lhsTypeTag) && TypeTags.BYTE_TAG == rhsTypeTag) { + return Long.compare((long) lhsValue, (int) rhsValue); + } else if (TypeTags.isIntegerTypeTag(lhsTypeTag) && TypeTags.isIntegerTypeTag(rhsTypeTag)) { + return Long.compare((long) lhsValue, (long) rhsValue); + } + if ((lhsTypeTag == TypeTags.ARRAY_TAG || lhsTypeTag == TypeTags.TUPLE_TAG) && (rhsTypeTag == TypeTags.ARRAY_TAG || rhsTypeTag == TypeTags.TUPLE_TAG)) { return compareArrayValues(lhsValue, rhsValue, lhsTypeTag, rhsTypeTag, direction); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java index ec772ed9b133..b610c8a18316 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/utils/ValueConverter.java @@ -31,6 +31,10 @@ import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.types.TypedescType; +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.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -57,6 +61,7 @@ import io.ballerina.runtime.internal.values.ReadOnlyUtils; import io.ballerina.runtime.internal.values.TableValueImpl; import io.ballerina.runtime.internal.values.TupleValueImpl; +import io.ballerina.runtime.internal.values.XmlSequence; import java.util.ArrayList; import java.util.HashMap; @@ -146,6 +151,7 @@ private static Object convert(Object value, Type targetType, Set if (matchingType.isReadOnly()) { newValue = CloneUtils.cloneReadOnly(newValue); } + newValue = xmlSequenceHack(newValue, matchingType); break; } @@ -171,6 +177,27 @@ private static Object convert(Object value, Type targetType, Set return newValue; } + // This is a hack to workaround #43231 + private static Object xmlSequenceHack(Object value, Type targetType) { + if (!(value instanceof XmlSequence xmlSequence)) { + return value; + } + Context cx = TypeChecker.context(); + List list = new ArrayList<>(); + SemType targetSemType = SemType.tryInto(targetType); + for (BXml child : xmlSequence.getChildrenList()) { + SemType childType = SemType.tryInto(child.getType()); + boolean isReadonly = + Core.isSubType(cx, Core.intersect(childType, targetSemType), Builder.getReadonlyType()); + if (isReadonly) { + list.add((BXml) CloneUtils.cloneReadOnly(child)); + } else { + list.add(child); + } + } + return new XmlSequence(list); + } + private static Type getTargetFromTypeDesc(Type targetType) { Type referredType = TypeUtils.getImpliedType(targetType); if (referredType.getTag() == TypeTags.TYPEDESC_TAG) { diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractArrayValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractArrayValue.java index 4768e51ecd52..a129148baf23 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractArrayValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractArrayValue.java @@ -20,12 +20,17 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.internal.errors.ErrorHelper; import io.ballerina.runtime.internal.json.JsonGenerator; import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BUnionType; +import io.ballerina.runtime.internal.types.TypeWithShape; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; import io.ballerina.runtime.internal.utils.IteratorUtils; import java.io.ByteArrayOutputStream; @@ -33,6 +38,7 @@ import java.util.ArrayList; import java.util.LinkedHashSet; import java.util.Map; +import java.util.Optional; import java.util.Set; import static io.ballerina.runtime.api.constants.RuntimeConstants.ARRAY_LANG_LIB; @@ -51,9 +57,10 @@ * * @since 1.1.0 */ -public abstract class AbstractArrayValue implements ArrayValue { +public abstract class AbstractArrayValue implements ArrayValue, RecursiveValue { static final int SYSTEM_ARRAY_MAX = Integer.MAX_VALUE - 8; + private final ThreadLocal readonlyAttachedDefinition = new ThreadLocal<>(); /** * The maximum size of arrays to allocate. @@ -77,6 +84,9 @@ public void append(Object value) { @Override public boolean equals(Object o, Set visitedValues) { + if (!(o instanceof ArrayValue arrayValue)) { + return false; + } ValuePair compValuePair = new ValuePair(this, o); for (ValuePair valuePair : visitedValues) { if (valuePair.equals(compValuePair)) { @@ -85,7 +95,6 @@ public boolean equals(Object o, Set visitedValues) { } visitedValues.add(compValuePair); - ArrayValue arrayValue = (ArrayValue) o; if (arrayValue.size() != this.size()) { return false; } @@ -303,4 +312,25 @@ public boolean hasNext() { return cursor < length; } } + + @Override + public synchronized ListDefinition getReadonlyShapeDefinition() { + return readonlyAttachedDefinition.get(); + } + + @Override + public synchronized void setReadonlyShapeDefinition(ListDefinition definition) { + readonlyAttachedDefinition.set(definition); + } + + @Override + public synchronized void resetReadonlyShapeDefinition() { + readonlyAttachedDefinition.remove(); + } + + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) getType(); + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractObjectValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractObjectValue.java index 07d20399f30d..5457ca79574e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractObjectValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/AbstractObjectValue.java @@ -23,6 +23,9 @@ import io.ballerina.runtime.api.types.ObjectType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeId; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -35,10 +38,13 @@ import io.ballerina.runtime.internal.errors.ErrorCodes; import io.ballerina.runtime.internal.errors.ErrorHelper; import io.ballerina.runtime.internal.types.BObjectType; +import io.ballerina.runtime.internal.types.TypeWithShape; +import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.StringJoiner; import static io.ballerina.runtime.api.constants.RuntimeConstants.DOT; @@ -57,10 +63,12 @@ * * @since 0.995.0 */ -public abstract class AbstractObjectValue implements ObjectValue { +public abstract class AbstractObjectValue implements ObjectValue, RecursiveValue { private BTypedesc typedesc; private final BObjectType objectType; private final Type type; + private SemType shape; + private final ThreadLocal readonlyAttachedDefinition = new ThreadLocal<>(); private final HashMap nativeData = new HashMap<>(); @@ -233,4 +241,33 @@ private void checkFieldUpdateType(String fieldName, Object value) { ErrorHelper.getErrorDetails(ErrorCodes.INVALID_OBJECT_FIELD_VALUE_ERROR, fieldName, fieldType, TypeChecker.getType(value))); } + + public final SemType shapeOf() { + return shape; + } + + public final void cacheShape(SemType semType) { + this.shape = semType; + } + + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) getType(); + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } + + @Override + public ObjectDefinition getReadonlyShapeDefinition() { + return readonlyAttachedDefinition.get(); + } + + @Override + public void setReadonlyShapeDefinition(ObjectDefinition definition) { + readonlyAttachedDefinition.set(definition); + } + + @Override + public void resetReadonlyShapeDefinition() { + readonlyAttachedDefinition.remove(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValueImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValueImpl.java index ad1896b75e76..6413e10fd648 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValueImpl.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ArrayValueImpl.java @@ -24,6 +24,7 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -86,6 +87,8 @@ public class ArrayValueImpl extends AbstractArrayValue { private double[] floatValues; private BString[] bStringValues; private BTypedesc typedesc; + + private SemType shape; // ------------------------ Constructors ------------------------------------------------------------------- public ArrayValueImpl(Object[] values, ArrayType type) { @@ -614,15 +617,15 @@ public void addRefValue(long index, Object value) { Type type = TypeChecker.getType(value); switch (this.elementReferredType.getTag()) { case TypeTags.BOOLEAN_TAG: - prepareForAdd(index, value, type, booleanValues.length); + prepareForAdd(index, value, booleanValues.length); this.booleanValues[(int) index] = (Boolean) value; return; case TypeTags.FLOAT_TAG: - prepareForAdd(index, value, type, floatValues.length); + prepareForAdd(index, value, floatValues.length); this.floatValues[(int) index] = (Double) value; return; case TypeTags.BYTE_TAG: - prepareForAdd(index, value, type, byteValues.length); + prepareForAdd(index, value, byteValues.length); this.byteValues[(int) index] = ((Number) value).byteValue(); return; case TypeTags.INT_TAG: @@ -632,16 +635,16 @@ public void addRefValue(long index, Object value) { case TypeTags.UNSIGNED32_INT_TAG: case TypeTags.UNSIGNED16_INT_TAG: case TypeTags.UNSIGNED8_INT_TAG: - prepareForAdd(index, value, type, intValues.length); + prepareForAdd(index, value, intValues.length); this.intValues[(int) index] = (Long) value; return; case TypeTags.STRING_TAG: case TypeTags.CHAR_STRING_TAG: - prepareForAdd(index, value, type, bStringValues.length); + prepareForAdd(index, value, bStringValues.length); this.bStringValues[(int) index] = (BString) value; return; default: - prepareForAdd(index, value, type, refValues.length); + prepareForAdd(index, value, refValues.length); this.refValues[(int) index] = value; } } @@ -659,27 +662,27 @@ public void setArrayRefTypeForcefully(ArrayType type, int size) { public void addInt(long index, long value) { if (intValues != null) { - prepareForAdd(index, value, PredefinedTypes.TYPE_INT, intValues.length); + prepareForAdd(index, value, intValues.length); intValues[(int) index] = value; return; } - prepareForAdd(index, value, TypeChecker.getType(value), byteValues.length); + prepareForAdd(index, value, byteValues.length); byteValues[(int) index] = (byte) ((Long) value).intValue(); } private void addBoolean(long index, boolean value) { - prepareForAdd(index, value, PredefinedTypes.TYPE_BOOLEAN, booleanValues.length); + prepareForAdd(index, value, booleanValues.length); booleanValues[(int) index] = value; } private void addByte(long index, byte value) { - prepareForAdd(index, value, PredefinedTypes.TYPE_BYTE, byteValues.length); + prepareForAdd(index, value, byteValues.length); byteValues[(int) index] = value; } private void addFloat(long index, double value) { - prepareForAdd(index, value, PredefinedTypes.TYPE_FLOAT, floatValues.length); + prepareForAdd(index, value, floatValues.length); floatValues[(int) index] = value; } @@ -689,7 +692,7 @@ private void addString(long index, String value) { } private void addBString(long index, BString value) { - prepareForAdd(index, value, PredefinedTypes.TYPE_STRING, bStringValues.length); + prepareForAdd(index, value, bStringValues.length); bStringValues[(int) index] = value; } @@ -1257,12 +1260,12 @@ protected void unshift(long index, Object[] vals) { // Private methods - private void prepareForAdd(long index, Object value, Type sourceType, int currentArraySize) { + private void prepareForAdd(long index, Object value, int currentArraySize) { // check types - if (!TypeChecker.checkIsType(null, value, sourceType, this.elementType)) { + if (!TypeChecker.checkIsType(value, this.elementType)) { throw ErrorCreator.createError(getModulePrefixedReason(ARRAY_LANG_LIB, INHERENT_TYPE_VIOLATION_ERROR_IDENTIFIER), ErrorHelper.getErrorDetails( - ErrorCodes.INCOMPATIBLE_TYPE, this.elementType, sourceType)); + ErrorCodes.INCOMPATIBLE_TYPE, this.elementType, TypeChecker.getType(value))); } prepareForAddWithoutTypeCheck(index, currentArraySize); } @@ -1406,4 +1409,14 @@ private int calculateHashCode(List visited) { } return result; } + + @Override + public void cacheShape(SemType semType) { + shape = semType; + } + + @Override + public SemType shapeOf() { + return shape; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/DecimalValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/DecimalValue.java index ec48ef7789af..d5e5e918e02e 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/DecimalValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/DecimalValue.java @@ -22,17 +22,22 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BDecimal; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.internal.errors.ErrorCodes; import io.ballerina.runtime.internal.errors.ErrorHelper; import io.ballerina.runtime.internal.errors.ErrorReasons; +import io.ballerina.runtime.internal.types.BDecimalType; import io.ballerina.runtime.internal.utils.ErrorUtils; import java.math.BigDecimal; import java.math.MathContext; import java.math.RoundingMode; import java.util.Map; +import java.util.Optional; /** *

@@ -60,8 +65,10 @@ public class DecimalValue implements SimpleValue, BDecimal { public DecimalValueKind valueKind = DecimalValueKind.OTHER; private final BigDecimal value; + private final Type singletonType; public DecimalValue(BigDecimal value) { + this.singletonType = BDecimalType.singletonType(value); this.value = getValidDecimalValue(value); if (!this.booleanValue()) { this.valueKind = DecimalValueKind.ZERO; @@ -83,7 +90,7 @@ public DecimalValue(String value) { throw exception; } this.value = getValidDecimalValue(bd); - + this.singletonType = BDecimalType.singletonType(this.value); if (!this.booleanValue()) { this.valueKind = DecimalValueKind.ZERO; } @@ -229,7 +236,7 @@ public BigDecimal value() { */ @Override public Type getType() { - return PredefinedTypes.TYPE_DECIMAL; + return singletonType; } //========================= Mathematical operations supported =============================== @@ -480,4 +487,9 @@ public static DecimalValue valueOfJ(BigDecimal value) { return new DecimalValue(new BigDecimal(value.toString(), MathContext.DECIMAL128) .setScale(1, BigDecimal.ROUND_HALF_EVEN)); } + + @Override + public Optional inherentTypeOf(Context cx) { + return Optional.of(Builder.getDecimalConst(value)); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ErrorValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ErrorValue.java index f8cd391a127a..140cd7d0dc08 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ErrorValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ErrorValue.java @@ -86,7 +86,7 @@ public class ErrorValue extends BError implements RefValue { private static final String STOP_FUNCTION_SUFFIX = "."; public ErrorValue(BString message) { - this(new BErrorType(TypeConstants.ERROR, PredefinedTypes.TYPE_ERROR.getPackage(), TYPE_MAP), + this(new BErrorType(TypeConstants.ERROR, PredefinedTypes.TYPE_ERROR.getPackage(), PredefinedTypes.TYPE_DETAIL), message, null, new MapValueImpl<>(PredefinedTypes.TYPE_ERROR_DETAIL)); } @@ -469,7 +469,9 @@ private boolean isCompilerAddedName(String name) { */ @Override public boolean equals(Object o, Set visitedValues) { - ErrorValue errorValue = (ErrorValue) o; + if (!(o instanceof ErrorValue errorValue)) { + return false; + } return isEqual(this.getMessage(), errorValue.getMessage(), visitedValues) && ((MapValueImpl) this.getDetails()).equals(errorValue.getDetails(), visitedValues) && isEqual(this.getCause(), errorValue.getCause(), visitedValues); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java index 35dd9a28be60..6e116666a45b 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/FPValue.java @@ -20,12 +20,15 @@ import io.ballerina.runtime.api.Runtime; import io.ballerina.runtime.api.constants.RuntimeConstants; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BFunctionPointer; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.api.values.BTypedesc; import io.ballerina.runtime.internal.BalRuntime; import java.util.Map; +import java.util.Optional; import java.util.function.Function; /** @@ -101,4 +104,9 @@ public String getName() { public String toString() { return RuntimeConstants.EMPTY; } + + @Override + public Optional inherentTypeOf(Context cx) { + return Optional.of(SemType.tryInto(getType())); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValueImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValueImpl.java index ebbdce07f208..30630dc86aa8 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValueImpl.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/MapValueImpl.java @@ -19,9 +19,13 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BError; @@ -41,10 +45,11 @@ import io.ballerina.runtime.internal.json.JsonInternalUtils; import io.ballerina.runtime.internal.scheduling.Scheduler; import io.ballerina.runtime.internal.types.BField; -import io.ballerina.runtime.internal.types.BMapType; import io.ballerina.runtime.internal.types.BRecordType; import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BUnionType; +import io.ballerina.runtime.internal.types.TypeWithShape; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; import io.ballerina.runtime.internal.utils.CycleUtils; import io.ballerina.runtime.internal.utils.IteratorUtils; import io.ballerina.runtime.internal.utils.MapUtils; @@ -60,6 +65,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.StringJoiner; import java.util.stream.Collectors; @@ -93,13 +99,15 @@ * @since 0.995.0 */ public class MapValueImpl extends LinkedHashMap implements RefValue, CollectionValue, MapValue, - BMap { + BMap, RecursiveValue { private BTypedesc typedesc; private Type type; private Type referredType; private final Map nativeData = new HashMap<>(); private Type iteratorNextReturnType; + private SemType shape; + private final ThreadLocal readonlyAttachedDefinition = new ThreadLocal<>(); public MapValueImpl(TypedescValue typedesc) { this(typedesc.getDescribingType()); @@ -233,7 +241,7 @@ public V fillAndGet(Object key) { expectedType = recordType.restFieldType; } } else { - expectedType = ((BMapType) this.referredType).getConstrainedType(); + expectedType = ((MapType) this.referredType).getConstrainedType(); } if (!TypeChecker.hasFillerValue(expectedType)) { @@ -347,7 +355,7 @@ protected void populateInitialValues(BMapInitialValueEntry[] initialValues) { @Override public void populateInitialValue(K key, V value) { if (referredType.getTag() == TypeTags.MAP_TAG) { - MapUtils.handleInherentTypeViolatingMapUpdate(value, (BMapType) referredType); + MapUtils.handleInherentTypeViolatingMapUpdate(value, (MapType) referredType); putValue(key, value); } else { BString fieldName = (BString) key; @@ -609,6 +617,21 @@ public IteratorValue getIterator() { return new MapIterator<>(new LinkedHashSet<>(this.entrySet()).iterator()); } + @Override + public synchronized MappingDefinition getReadonlyShapeDefinition() { + return readonlyAttachedDefinition.get(); + } + + @Override + public synchronized void setReadonlyShapeDefinition(MappingDefinition definition) { + readonlyAttachedDefinition.set(definition); + } + + @Override + public synchronized void resetReadonlyShapeDefinition() { + readonlyAttachedDefinition.remove(); + } + /** * {@link MapIterator} iteration provider for ballerina maps. * @@ -688,7 +711,7 @@ public Map getNativeDataMap() { private void initializeIteratorNextReturnType() { Type type; if (this.referredType.getTag() == PredefinedTypes.TYPE_MAP.getTag()) { - BMapType mapType = (BMapType) this.referredType; + MapType mapType = (MapType) this.referredType; type = mapType.getConstrainedType(); } else { BRecordType recordType = (BRecordType) this.referredType; @@ -722,4 +745,20 @@ public Type getIteratorNextReturnType() { protected V putValue(K key, V value) { return super.put(key, value); } + + @Override + public void cacheShape(SemType semType) { + shape = semType; + } + + @Override + public SemType shapeOf() { + return shape; + } + + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) type; + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ReadOnlyUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ReadOnlyUtils.java index c9ba580378da..8e23ef16c232 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ReadOnlyUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/ReadOnlyUtils.java @@ -26,6 +26,7 @@ import io.ballerina.runtime.api.types.Field; import io.ballerina.runtime.api.types.IntersectableReferenceType; import io.ballerina.runtime.api.types.IntersectionType; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.ReferenceType; import io.ballerina.runtime.api.types.SelectivelyImmutableReferenceType; @@ -184,9 +185,9 @@ private static BIntersectionType setImmutableIntersectionType(Type type, Set Type of the definition + * + * @since 2201.11.0 + */ +interface RecursiveValue { + + E getReadonlyShapeDefinition(); + + void setReadonlyShapeDefinition(E definition); + + void resetReadonlyShapeDefinition(); +} diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java index c9a4fd8f30c7..a00800f64b53 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/RegExpValue.java @@ -17,12 +17,17 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.api.values.BRegexpValue; import io.ballerina.runtime.api.values.BTypedesc; +import io.ballerina.runtime.internal.types.semtype.RegexUtils; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import static io.ballerina.runtime.internal.utils.ValueUtils.getTypedescValue; @@ -41,9 +46,11 @@ public class RegExpValue implements BRegexpValue, RefValue { private final RegExpDisjunction regExpDisjunction; private BTypedesc typedesc; private static final Type type = PredefinedTypes.TYPE_READONLY_ANYDATA; + private final SemType shape; public RegExpValue(RegExpDisjunction regExpDisjunction) { this.regExpDisjunction = regExpDisjunction; + this.shape = RegexUtils.regexShape(regExpDisjunction.stringValue(null)); } @Override @@ -76,11 +83,6 @@ public int hashCode() { return Objects.hash(this.regExpDisjunction); } - @Override - public boolean equals(Object obj) { - return this == obj; - } - @Override public BTypedesc getTypedesc() { if (this.typedesc == null) { @@ -123,4 +125,19 @@ public boolean equals(Object o, Set visitedValues) { } return this.stringValue(null).equals(rhsRegExpValue.stringValue(null)); } + + @Override + public SemType widenedType() { + return Builder.getRegexType(); + } + + @Override + public Optional shapeOf() { + return Optional.of(this.shape); + } + + @Override + public Optional inherentTypeOf(Context cx) { + return shapeOf(); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/StringValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/StringValue.java index 3898b3aa8ca4..d553b300f4a8 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/StringValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/StringValue.java @@ -17,12 +17,16 @@ */ package io.ballerina.runtime.internal.values; -import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; +import io.ballerina.runtime.api.types.semtype.Builder; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.api.values.BString; +import io.ballerina.runtime.internal.types.BStringType; import java.util.Map; +import java.util.Optional; /** * Class representing ballerina strings. @@ -33,15 +37,19 @@ public abstract class StringValue implements BString, SimpleValue { final String value; final boolean isNonBmp; + private final Type type; + private final SemType shape; protected StringValue(String value, boolean isNonBmp) { this.value = value; this.isNonBmp = isNonBmp; + this.type = BStringType.singletonType(value); + this.shape = Builder.getStringConst(value); } @Override public Type getType() { - return PredefinedTypes.TYPE_STRING; + return type; } @Override @@ -100,4 +108,8 @@ public boolean equals(Object str) { return false; } + @Override + public Optional inherentTypeOf(Context cx) { + return Optional.of(shape); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TableValueImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TableValueImpl.java index c027da267680..5ce57d89f406 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TableValueImpl.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TableValueImpl.java @@ -20,9 +20,13 @@ import io.ballerina.runtime.api.creators.ErrorCreator; import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.Field; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.TableType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -37,12 +41,12 @@ import io.ballerina.runtime.internal.errors.ErrorCodes; import io.ballerina.runtime.internal.errors.ErrorHelper; import io.ballerina.runtime.internal.types.BIntersectionType; -import io.ballerina.runtime.internal.types.BMapType; import io.ballerina.runtime.internal.types.BRecordType; import io.ballerina.runtime.internal.types.BTableType; import io.ballerina.runtime.internal.types.BTupleType; import io.ballerina.runtime.internal.types.BTypeReferenceType; import io.ballerina.runtime.internal.types.BUnionType; +import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.utils.CycleUtils; import io.ballerina.runtime.internal.utils.IteratorUtils; import io.ballerina.runtime.internal.utils.TableUtils; @@ -58,6 +62,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.StringJoiner; import java.util.TreeMap; @@ -430,7 +435,7 @@ private Type getTableConstraintField(Type constraintType, String fieldName) { Map fieldList = ((BRecordType) constraintType).getFields(); return fieldList.get(fieldName).getFieldType(); case TypeTags.MAP_TAG: - return ((BMapType) constraintType).getConstrainedType(); + return ((MapType) constraintType).getConstrainedType(); case TypeTags.INTERSECTION_TAG: Type effectiveType = ((BIntersectionType) constraintType).getEffectiveType(); return getTableConstraintField(effectiveType, fieldName); @@ -788,7 +793,7 @@ public MultiKeyWrapper() { Arrays.stream(fieldNames) .forEach(field -> keyTypes.add(recordType.getFields().get(field).getFieldType())); } else if (constraintType.getTag() == TypeTags.MAP_TAG) { - BMapType mapType = (BMapType) constraintType; + MapType mapType = (MapType) constraintType; Arrays.stream(fieldNames).forEach(field -> keyTypes.add(mapType.getConstrainedType())); } keyType = new BTupleType(keyTypes); @@ -904,4 +909,10 @@ public BObject getObjectValue(BString key) { public BArray getArrayValue(BString key) { return (BArray) get(key); } + + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) type; + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TupleValueImpl.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TupleValueImpl.java index b6db397f4617..6086c28633ad 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TupleValueImpl.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/TupleValueImpl.java @@ -21,6 +21,7 @@ import io.ballerina.runtime.api.types.TupleType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; +import io.ballerina.runtime.api.types.semtype.SemType; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; import io.ballerina.runtime.api.values.BArray; @@ -75,6 +76,7 @@ public class TupleValueImpl extends AbstractArrayValue { private final boolean hasRestElement; // cached value for ease of access private BTypedesc typedesc; private TypedescValueImpl inherentType; + private SemType shape; // ------------------------ Constructors ------------------------------------------------------------------- public TupleValueImpl(Object[] values, TupleType type) { @@ -871,4 +873,14 @@ private void validateInherentTypeOfExistingMembers(int index, int offset) { } } } + + @Override + public void cacheShape(SemType semType) { + shape = semType; + } + + @Override + public SemType shapeOf() { + return shape; + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlValue.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlValue.java index 30c606d8c4b2..ea862ca298db 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlValue.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/internal/values/XmlValue.java @@ -20,6 +20,9 @@ import io.ballerina.runtime.api.types.PredefinedTypes; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.XmlNodeType; +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.api.types.semtype.ShapeAnalyzer; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BLink; import io.ballerina.runtime.api.values.BMap; @@ -27,12 +30,14 @@ import io.ballerina.runtime.api.values.BTypedesc; import io.ballerina.runtime.api.values.BXml; import io.ballerina.runtime.api.values.BXmlQName; +import io.ballerina.runtime.internal.types.TypeWithShape; import io.ballerina.runtime.internal.utils.IteratorUtils; import io.ballerina.runtime.internal.xml.BallerinaXmlSerializer; import java.io.OutputStream; import java.util.List; import java.util.Map; +import java.util.Optional; import javax.xml.namespace.QName; @@ -75,30 +80,32 @@ public BString getAttribute(BXmlQName attributeName) { } /** - * Set the value of a single attribute. If the attribute already exsists, then the value will be updated. + * Set the value of a single attribute. If the attribute already exsists, then + * the value will be updated. * Otherwise a new attribute will be added. * * @param attributeName Qualified name of the attribute - * @param value Value of the attribute + * @param value Value of the attribute */ @Override @Deprecated public void setAttribute(BXmlQName attributeName, String value) { setAttributeOnInitialization(attributeName.getLocalName(), attributeName.getUri(), attributeName.getPrefix(), - value); + value); } /** - * Set the value of a single attribute. If the attribute already exsists, then the value will be updated. + * Set the value of a single attribute. If the attribute already exsists, then + * the value will be updated. * Otherwise a new attribute will be added. * * @param attributeName Qualified name of the attribute - * @param value Value of the attribute + * @param value Value of the attribute */ @Deprecated public void setAttribute(BXmlQName attributeName, BString value) { setAttributeOnInitialization(attributeName.getLocalName(), attributeName.getUri(), attributeName.getPrefix(), - value.getValue()); + value.getValue()); } /** @@ -147,12 +154,13 @@ public Type getType() { protected abstract void setAttributesOnInitialization(BMap attributes); protected abstract void setAttributeOnInitialization(String localName, String namespace, String prefix, - String value); + String value); // private methods protected static void handleXmlException(String message, Throwable t) { - // Here local message of the cause is logged whenever possible, to avoid java class being logged + // Here local message of the cause is logged whenever possible, to avoid java + // class being logged // along with the error message. if (t.getCause() != null) { throw ErrorCreator.createError(StringUtils.fromString(message + t.getCause().getMessage())); @@ -184,10 +192,12 @@ protected QName getQname(String qname) { } /** - * Recursively traverse and add the descendant with the given name to the descendants list. - * @param descendants List to add descendants + * Recursively traverse and add the descendant with the given name to the + * descendants list. + * + * @param descendants List to add descendants * @param currentElement Current node - * @param qnames Qualified names of the descendants to search + * @param qnames Qualified names of the descendants to search */ protected void addDescendants(List descendants, XmlItem currentElement, List qnames) { for (BXml child : currentElement.getChildrenSeq().getChildrenList()) { @@ -273,4 +283,9 @@ public Type getIteratorNextReturnType() { return iteratorNextReturnType; } + @Override + public Optional inherentTypeOf(Context cx) { + TypeWithShape typeWithShape = (TypeWithShape) type; + return typeWithShape.inherentTypeOf(cx, ShapeAnalyzer::inherentTypeOf, this); + } } diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/BSpan.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/BSpan.java index 1b9ecc424ceb..50b9be5606fe 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/BSpan.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/BSpan.java @@ -31,6 +31,7 @@ import io.opentelemetry.api.trace.SpanBuilder; import io.opentelemetry.api.trace.SpanContext; import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.api.trace.StatusCode; import io.opentelemetry.api.trace.Tracer; import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.TextMapGetter; @@ -149,6 +150,10 @@ public void addEvent(String eventName, Attributes attributes) { span.addEvent(eventName, attributes); } + public void setStatus(StatusCode statusCode) { + span.setStatus(statusCode); + } + public void addTags(Map tags) { for (Map.Entry entry : tags.entrySet()) { span.setAttribute(entry.getKey(), entry.getValue()); diff --git a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/TracingUtils.java b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/TracingUtils.java index 1747c16ac9b4..944e804d333f 100644 --- a/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/TracingUtils.java +++ b/bvm/ballerina-runtime/src/main/java/io/ballerina/runtime/observability/tracer/TracingUtils.java @@ -20,6 +20,7 @@ import io.ballerina.runtime.internal.values.ErrorValue; import io.ballerina.runtime.observability.ObserverContext; import io.ballerina.runtime.observability.metrics.Tag; +import io.opentelemetry.api.trace.StatusCode; import java.util.Collections; import java.util.Map; @@ -79,6 +80,9 @@ public static void stopObservation(ObserverContext observerContext) { ErrorValue bError = (ErrorValue) observerContext.getProperty(PROPERTY_ERROR_VALUE); if (bError != null) { span.addTag(TAG_KEY_STR_ERROR_MESSAGE, bError.getPrintableStackTrace()); + span.setStatus(StatusCode.ERROR); + } else { + span.setStatus(StatusCode.OK); } // Adding specific error code to Trace Span diff --git a/bvm/ballerina-runtime/src/main/java/module-info.java b/bvm/ballerina-runtime/src/main/java/module-info.java index d09fee04a70a..fbb04210f3f4 100644 --- a/bvm/ballerina-runtime/src/main/java/module-info.java +++ b/bvm/ballerina-runtime/src/main/java/module-info.java @@ -28,6 +28,7 @@ exports io.ballerina.runtime.api.types; exports io.ballerina.runtime.api.utils; exports io.ballerina.runtime.api.values; + exports io.ballerina.runtime.api.types.semtype; exports io.ballerina.runtime.observability; exports io.ballerina.runtime.observability.metrics; @@ -62,4 +63,5 @@ exports io.ballerina.runtime.internal to ballerina.debug.adapter.core, io.ballerina.cli, io.ballerina.cli.utils, io.ballerina.java, io.ballerina.lang, io.ballerina.lang.array, io.ballerina.lang.bool, io.ballerina.lang.decimal, io.ballerina.lang.error, io.ballerina.lang.floatingpoint, io.ballerina.lang.function, io.ballerina.lang.integer, io.ballerina.lang.internal, io.ballerina.lang.map, io.ballerina.lang.regexp, io.ballerina.lang.table, io.ballerina.lang.test, io.ballerina.lang.transaction, io.ballerina.lang.value, io.ballerina.lang.xml, io.ballerina.log.api, io.ballerina.runtime.profiler, io.ballerina.shell, io.ballerina.testerina.core, io.ballerina.testerina.runtime, org.ballerinalang.debugadapter.runtime; exports io.ballerina.runtime.api.repository; exports io.ballerina.runtime.internal.repository to ballerina.debug.adapter.core, io.ballerina.cli, io.ballerina.cli.utils, io.ballerina.java, io.ballerina.lang, io.ballerina.lang.array, io.ballerina.lang.bool, io.ballerina.lang.decimal, io.ballerina.lang.error, io.ballerina.lang.floatingpoint, io.ballerina.lang.function, io.ballerina.lang.integer, io.ballerina.lang.internal, io.ballerina.lang.map, io.ballerina.lang.regexp, io.ballerina.lang.table, io.ballerina.lang.test, io.ballerina.lang.transaction, io.ballerina.lang.value, io.ballerina.lang.xml, io.ballerina.log.api, io.ballerina.runtime.profiler, io.ballerina.shell, io.ballerina.testerina.core, io.ballerina.testerina.runtime, org.ballerinalang.debugadapter.runtime; + exports io.ballerina.runtime.internal.types.semtype to io.ballerina.runtime.api.types.semtype, io.ballerina.runtime.internal.types; } diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/TomlProviderTest.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/TomlProviderTest.java index 8c4559c5a0dd..e14146289491 100644 --- a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/TomlProviderTest.java +++ b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/TomlProviderTest.java @@ -571,7 +571,7 @@ public void testTomlProviderWithString() { @Test(dataProvider = "map-data-provider") public void testTomlProviderMaps(String variableName, Type constraint, Map expectedValues) { - MapType type = TypeCreator.createMapType("MapType", constraint, ROOT_MODULE, false); + MapType type = TypeCreator.createMapType(variableName + "Type", constraint, ROOT_MODULE, false); IntersectionType mapType = new BIntersectionType(ROOT_MODULE, new Type[]{type, PredefinedTypes.TYPE_READONLY} , type, 1, true); VariableKey mapVar = new VariableKey(ROOT_MODULE, variableName, mapType, true); diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/negative/TomlProviderNegativeTest.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/negative/TomlProviderNegativeTest.java index a4d9fd911b17..4a16435e7be8 100644 --- a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/negative/TomlProviderNegativeTest.java +++ b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/config/negative/TomlProviderNegativeTest.java @@ -283,7 +283,7 @@ public void testTableNegativeConfig(String tomlFileName, String[] errorMessages) Field age = TypeCreator.createField(PredefinedTypes.TYPE_INT, "age", SymbolFlags.OPTIONAL); Map fields = Map.ofEntries(Map.entry("name", name), Map.entry("age", age), Map.entry("id", id)); RecordType type = - TypeCreator.createRecordType("Person", ROOT_MODULE, SymbolFlags.READONLY, fields, null, true, 6); + TypeCreator.createRecordType("PersonTPNT", ROOT_MODULE, SymbolFlags.READONLY, fields, null, true, 6); TableType tableType = TypeCreator.createTableType(type, new String[]{"name"}, true); IntersectionType intersectionType = new BIntersectionType(ROOT_MODULE, new Type[]{tableType, PredefinedTypes.TYPE_READONLY}, tableType, 1, true); @@ -296,16 +296,14 @@ public void testTableNegativeConfig(String tomlFileName, String[] errorMessages) @DataProvider(name = "table-negative-tests") public Object[][] getTableNegativeTests() { - return new Object[][]{ - {"MissingTableKey", new String[] { - "[MissingTableKey.toml:(6:1,8:9)] value required for key 'name' of type " + - "'table key(name) & readonly' in configurable variable 'tableVar'", - "[MissingTableKey.toml:(7:1,7:8)] unused configuration value 'tableVar.id'", - "[MissingTableKey.toml:(8:1,8:9)] unused configuration value 'tableVar.age'" - }}, + return new Object[][]{{"MissingTableKey", new String[]{ + "[MissingTableKey.toml:(6:1,8:9)] value required for key 'name' of type " + + "'table key(name) & readonly' in configurable variable 'tableVar'", + "[MissingTableKey.toml:(7:1,7:8)] unused configuration value 'tableVar.id'", + "[MissingTableKey.toml:(8:1,8:9)] unused configuration value 'tableVar.age'"}}, {"TableTypeError", new String[] { "[TableTypeError.toml:(1:1,3:9)] configurable variable 'tableVar' is expected to be of type" + - " 'table key(name) & readonly', but found 'record'", + " 'table key(name) & readonly', but found 'record'", "[TableTypeError.toml:(2:1,2:14)] unused configuration value 'test_module.tableVar.name'", "[TableTypeError.toml:(3:1,3:9)] unused configuration value 'test_module.tableVar.age'" }}, @@ -321,11 +319,11 @@ public Object[][] getTableNegativeTests() { }}, {"AdditionalField", new String[] { "[AdditionalField.toml:(4:1,4:17)] undefined field 'city' provided for closed record " + - "'test_module:Person'" + "'test_module:PersonTPNT'" }}, {"MissingTableField", new String[] { "[MissingTableField.toml:(1:1,3:9)] value not provided for non-defaultable required field " + - "'id' of record 'test_module:Person' in configurable variable 'tableVar'" + "'id' of record 'test_module:PersonTPNT' in configurable variable 'tableVar'" }}, {"TableInlineTypeError1", new String[] { "[TableInlineTypeError1.toml:(1:34,1:37)] configurable variable 'tableVar.name' is expected " + @@ -337,7 +335,7 @@ public Object[][] getTableNegativeTests() { }}, {"TableInlineTypeError3", new String[] { "[TableInlineTypeError3.toml:(1:24,1:53)] configurable variable 'tableVar' is expected to be " + - "of type 'table key(name) & readonly', but found 'array'" + "of type 'table key(name) & readonly', but found 'array'" }}, }; } @@ -634,7 +632,7 @@ public void testInvalidIntersectionArray() { @Test public void testRestFieldInvalidType() { - RecordType recordType = TypeCreator.createRecordType("Person", ROOT_MODULE, SymbolFlags.READONLY, + RecordType recordType = TypeCreator.createRecordType("PersonTPNT2", ROOT_MODULE, SymbolFlags.READONLY, new HashMap<>(), PredefinedTypes.TYPE_INT, false, 6); VariableKey recordVar = new VariableKey(ROOT_MODULE, "person", recordType, true); String error = "[RestFieldNegative.toml:(3:8,3:14)] configurable variable 'person.name' is expected to be of " + diff --git a/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/semtype/CoreTests.java b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/semtype/CoreTests.java new file mode 100644 index 000000000000..fba49fa5c9e2 --- /dev/null +++ b/bvm/ballerina-runtime/src/test/java/io/ballerina/runtime/test/semtype/CoreTests.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.runtime.test.semtype; + +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.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import org.testng.annotations.Test; + +public class CoreTests { + + @Test + public void testCellTypes() { + Env env = Env.getInstance(); + Context cx = Context.from(env); + SemType intTy = Builder.getIntType(); + SemType readonlyInt = Builder.getCellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + assert Core.isSubType(cx, readonlyInt, readonlyInt); + SemType mutableInt = Builder.getCellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + assert Core.isSubType(cx, mutableInt, mutableInt); + assert Core.isSubType(cx, readonlyInt, mutableInt); + assert !Core.isSubType(cx, mutableInt, readonlyInt); + } + + @Test + public void testCellTypeCaching() { + Env env = Env.getInstance(); + SemType intTy = Builder.getIntType(); + SemType readonlyInt1 = Builder.getCellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + SemType readonlyInt2 = Builder.getCellContaining(env, intTy, CellAtomicType.CellMutability.CELL_MUT_NONE); + assert readonlyInt1 == readonlyInt2; + } + + @Test + public void testSimpleList() { + Env env = Env.getInstance(); + SemType intTy = Builder.getIntType(); + // int[] + ListDefinition ld = new ListDefinition(); + SemType intListTy = + ld.defineListTypeWrapped(env, new SemType[0], 0, intTy, + CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + + // int[1] + ListDefinition ld1 = new ListDefinition(); + SemType[] members = {intTy}; + SemType intListTy1 = + ld1.defineListTypeWrapped(env, members, 1, Builder.getNeverType(), + CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + + Context cx = Context.from(env); + assert Core.isSubType(cx, intListTy1, intListTy); + } +} diff --git a/compiler/ballerina-lang/build.gradle b/compiler/ballerina-lang/build.gradle index 55f8b87b71ca..35fa4b1d947d 100644 --- a/compiler/ballerina-lang/build.gradle +++ b/compiler/ballerina-lang/build.gradle @@ -24,6 +24,7 @@ dependencies { implementation project(':ballerina-parser') implementation project(':ballerina-runtime') implementation project(':toml-parser') + implementation project(':semtypes') implementation project(':central-client') implementation project(':maven-resolver') implementation project(':identifier-util') diff --git a/compiler/ballerina-lang/spotbugs-exclude.xml b/compiler/ballerina-lang/spotbugs-exclude.xml index 01f5d928a3e3..5615cc897ee7 100644 --- a/compiler/ballerina-lang/spotbugs-exclude.xml +++ b/compiler/ballerina-lang/spotbugs-exclude.xml @@ -500,4 +500,11 @@ + + + + + + + diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java index 73bf9e881f9b..edd726b9b112 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java @@ -38,6 +38,7 @@ import io.ballerina.tools.text.LineRange; import io.ballerina.tools.text.TextDocument; import io.ballerina.tools.text.TextRange; +import io.ballerina.types.Core; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.symbols.SymbolKind; import org.ballerinalang.model.tree.NodeKind; @@ -53,7 +54,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.tree.BLangClassDefinition; @@ -537,12 +537,12 @@ private boolean isInlineSingletonType(BSymbol symbol) { // !(symbol.kind == SymbolKind.TYPE_DEF) is checked to exclude type defs BType type = org.wso2.ballerinalang.compiler.semantics.analyzer.Types.getImpliedType(symbol.type); return !(symbol.kind == SymbolKind.TYPE_DEF) && type.tag == TypeTags.FINITE && - ((BFiniteType) type).getValueSpace().size() == 1; + Core.singleShape((symbol.type).semType()).isPresent(); } private boolean isInlineErrorType(BSymbol symbol) { return getImpliedType(symbol.type).tag == TypeTags.ERROR && - Symbols.isFlagOn(symbol.type.flags, Flags.ANONYMOUS); + Symbols.isFlagOn(symbol.type.getFlags(), Flags.ANONYMOUS); } private boolean isTypeSymbol(BSymbol tSymbol) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/LangLibFunctionBinder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/LangLibFunctionBinder.java index 1118cbb37226..5bb6b0339c2c 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/LangLibFunctionBinder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/LangLibFunctionBinder.java @@ -130,7 +130,8 @@ private BInvokableType duplicateType(BInvokableType original, List n paramTypes.addAll(original.paramTypes); } - BInvokableType duplicate = new BInvokableType(paramTypes, original.restType, original.retType, null); + BInvokableType duplicate = + new BInvokableType(types.typeEnv(), paramTypes, original.restType, original.retType, null); BInvokableTypeSymbol originalSym = (BInvokableTypeSymbol) original.tsymbol; BInvokableTypeSymbol duplicateTSym = new BInvokableTypeSymbol(original.tag, originalSym.flags, originalSym.pkgID, duplicate, diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFactory.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFactory.java index b91b0c6824c4..8ab3097ff9e8 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFactory.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/SymbolFactory.java @@ -403,7 +403,7 @@ private boolean isReadonlyIntersectionArrayType(BType type) { type = Types.getReferredType(type); if (type.tag == TypeTags.INTERSECTION && type.tsymbol != null && type.tsymbol.getOrigin() == SymbolOrigin.VIRTUAL && - (type.flags & Flags.READONLY) == Flags.READONLY) { + Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { return true; } if (type.tag == TypeTags.ARRAY) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamFinder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamFinder.java index b11ed6ebfe97..d664cb2c237b 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamFinder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamFinder.java @@ -26,7 +26,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -37,7 +36,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -62,7 +60,7 @@ * * @since 2.0.0 */ -public class TypeParamFinder implements TypeVisitor { +public class TypeParamFinder extends TypeVisitor { private final Set visited = new HashSet<>(); private BType typeParam; @@ -99,10 +97,6 @@ public void visit(BArrayType bArrayType) { find(bArrayType.eType); } - @Override - public void visit(BBuiltInRefType bBuiltInRefType) { - } - @Override public void visit(BAnyType bAnyType) { } @@ -164,7 +158,7 @@ public void visit(BNeverType bNeverType) { } @Override - public void visit(BNilType bNilType) { + public void visitNilType(BType btype) { } @Override @@ -235,10 +229,6 @@ public void visit(BObjectType bObjectType) { } } - @Override - public void visit(BType bType) { - } - @Override public void visit(BFutureType bFutureType) { find(bFutureType.constraint); @@ -249,7 +239,7 @@ public void visit(BHandleType bHandleType) { } private void setContainsTypeParam(BType type) { - if (Symbols.isFlagOn(type.flags, Flags.TYPE_PARAM)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.TYPE_PARAM)) { this.typeParam = type; } } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamResolver.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamResolver.java index 9deeb9007caf..a15d5428db91 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamResolver.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/TypeParamResolver.java @@ -18,6 +18,7 @@ package io.ballerina.compiler.api.impl; +import io.ballerina.types.Env; import org.ballerinalang.model.symbols.AnnotationAttachmentSymbol; import org.ballerinalang.model.symbols.SymbolKind; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; @@ -30,7 +31,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -74,9 +74,12 @@ public class TypeParamResolver implements BTypeVisitor { private final Map boundTypes = new HashMap<>(); private final BType typeParam; private final Types types; + private final Env typeEnv; + public TypeParamResolver(BType typeParam, CompilerContext context) { this.typeParam = typeParam; types = Types.getInstance(context); + this.typeEnv = types.typeEnv(); } /** @@ -106,7 +109,7 @@ public BType resolve(BType typeParam, BType boundType) { @Override public BType visit(BType typeInSymbol, BType boundType) { - if (Symbols.isFlagOn(Flags.TYPE_PARAM, typeInSymbol.flags) + if (Symbols.isFlagOn(Flags.TYPE_PARAM, typeInSymbol.getFlags()) && types.isAssignable(typeInSymbol, this.typeParam)) { return boundType; } @@ -114,11 +117,6 @@ public BType visit(BType typeInSymbol, BType boundType) { return typeInSymbol; } - @Override - public BType visit(BBuiltInRefType typeInSymbol, BType boundType) { - return typeInSymbol; - } - @Override public BType visit(BAnyType typeInSymbol, BType boundType) { return typeInSymbol; @@ -137,7 +135,8 @@ public BType visit(BMapType typeInSymbol, BType boundType) { return typeInSymbol; } - return new BMapType(typeInSymbol.tag, boundConstraintType, typeInSymbol.tsymbol, typeInSymbol.flags); + return new BMapType(typeEnv, typeInSymbol.tag, boundConstraintType, typeInSymbol.tsymbol, + typeInSymbol.getFlags()); } @Override @@ -148,7 +147,7 @@ public BType visit(BXMLType typeInSymbol, BType boundType) { return typeInSymbol; } - return new BXMLType(boundConstraintType, typeInSymbol.tsymbol, typeInSymbol.flags); + return new BXMLType(boundConstraintType, typeInSymbol.tsymbol, typeInSymbol.getFlags()); } @Override @@ -164,8 +163,9 @@ public BType visit(BArrayType typeInSymbol, BType boundType) { return typeInSymbol; } - return new BArrayType(boundElemType, typeInSymbol.tsymbol, typeInSymbol.size, typeInSymbol.state, - typeInSymbol.flags); + return new BArrayType(typeEnv, boundElemType, typeInSymbol.tsymbol, + typeInSymbol.getSize(), + typeInSymbol.state, typeInSymbol.getFlags()); } @Override @@ -197,7 +197,7 @@ public BType visit(BObjectType typeInSymbol, BType boundType) { BObjectTypeSymbol newTypeSymbol = new BObjectTypeSymbol(objectTypeSymbol.tag, objectTypeSymbol.flags, objectTypeSymbol.name, objectTypeSymbol.pkgID, objectTypeSymbol.getType(), objectTypeSymbol.owner, objectTypeSymbol.pos, objectTypeSymbol.origin); - BObjectType newObjectType = new BObjectType(newTypeSymbol, typeInSymbol.flags); + BObjectType newObjectType = new BObjectType(typeEnv, newTypeSymbol, typeInSymbol.getFlags()); newObjectType.fields = newObjectFields; newTypeSymbol.attachedFuncs = newAttachedFuncs; @@ -217,7 +217,7 @@ public BType visit(BRecordType typeInSymbol, BType boundType) { } BType newRestType = resolve(typeInSymbol.restFieldType, boundType); - BRecordType newRecordType = new BRecordType(typeInSymbol.tsymbol, typeInSymbol.flags); + BRecordType newRecordType = new BRecordType(typeEnv, typeInSymbol.tsymbol, typeInSymbol.getFlags()); newRecordType.fields = newRecordFields; newRecordType.restFieldType = newRestType; @@ -244,8 +244,8 @@ public BType visit(BTupleType typeInSymbol, BType boundType) { return typeInSymbol; } - return new BTupleType(typeInSymbol.tsymbol, newTupleMembers, newRestType, typeInSymbol.flags, - typeInSymbol.isCyclic); + return new BTupleType(typeEnv, typeInSymbol.tsymbol, newTupleMembers, newRestType, + typeInSymbol.getFlags(), typeInSymbol.isCyclic); } @Override @@ -257,7 +257,7 @@ public BType visit(BStreamType typeInSymbol, BType boundType) { return typeInSymbol; } - return new BStreamType(typeInSymbol.tag, boundConstraintType, typeInSymbol.completionType, + return new BStreamType(typeEnv, typeInSymbol.tag, boundConstraintType, typeInSymbol.completionType, typeInSymbol.tsymbol); } @@ -270,8 +270,9 @@ public BType visit(BTableType typeInSymbol, BType boundType) { return typeInSymbol; } - BTableType bTableType = new BTableType(typeInSymbol.tag, boundConstraintType, typeInSymbol.tsymbol, - typeInSymbol.flags); + BTableType bTableType = new BTableType(typeEnv, boundConstraintType, + typeInSymbol.tsymbol, + typeInSymbol.getFlags()); bTableType.keyTypeConstraint = typeInSymbol.keyTypeConstraint; return bTableType; } @@ -311,7 +312,8 @@ public BType visit(BInvokableType typeInSymbol, BType boundType) { } invokableTypeSymbol.returnType = newReturnType; - BInvokableType type = new BInvokableType(newParamTypes, newRestParamType, newReturnType, invokableTypeSymbol); + BInvokableType type = new BInvokableType(typeEnv, newParamTypes, newRestParamType, newReturnType, + invokableTypeSymbol); invokableTypeSymbol.type = type; return type; @@ -336,7 +338,7 @@ public BType visit(BUnionType typeInSymbol, BType boundType) { return typeInSymbol; } - return BUnionType.create(typeInSymbol.tsymbol, newMembers); + return BUnionType.create(typeEnv, typeInSymbol.tsymbol, newMembers); } @Override diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/AbstractTypeSymbol.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/AbstractTypeSymbol.java index ff392b6d7f3d..95785eb48027 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/AbstractTypeSymbol.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/AbstractTypeSymbol.java @@ -134,7 +134,7 @@ public boolean equals(Object obj) { } Types types = Types.getInstance(this.context); - return types.isSameType(this.bType, ((AbstractTypeSymbol) obj).getBType()); + return types.isSameTypeIncludingTags(this.bType, ((AbstractTypeSymbol) obj).getBType()); } @Override diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaFunctionTypeSymbol.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaFunctionTypeSymbol.java index 273d3f54142b..90efd9c580df 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaFunctionTypeSymbol.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaFunctionTypeSymbol.java @@ -179,7 +179,7 @@ public String signature() { return this.signature; } - if (Symbols.isFlagOn(getBType().flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(getBType().getFlags(), Flags.ANY_FUNCTION)) { this.signature = "function"; return this.signature; } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaNilTypeSymbol.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaNilTypeSymbol.java index a692ca8493df..021df64920bc 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaNilTypeSymbol.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaNilTypeSymbol.java @@ -20,7 +20,7 @@ import io.ballerina.compiler.api.SymbolVisitor; import io.ballerina.compiler.api.symbols.NilTypeSymbol; import io.ballerina.compiler.api.symbols.TypeDescKind; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; +import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.util.CompilerContext; /** @@ -30,7 +30,7 @@ */ public class BallerinaNilTypeSymbol extends AbstractTypeSymbol implements NilTypeSymbol { - public BallerinaNilTypeSymbol(CompilerContext context, BNilType nilType) { + public BallerinaNilTypeSymbol(CompilerContext context, BType nilType) { super(context, TypeDescKind.NIL, nilType); } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaSingletonTypeSymbol.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaSingletonTypeSymbol.java index 6f9bda8b6dc4..c33968e035bf 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaSingletonTypeSymbol.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaSingletonTypeSymbol.java @@ -23,9 +23,7 @@ import io.ballerina.compiler.api.symbols.SingletonTypeSymbol; import io.ballerina.compiler.api.symbols.TypeDescKind; import io.ballerina.compiler.api.symbols.TypeSymbol; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -39,35 +37,26 @@ public class BallerinaSingletonTypeSymbol extends AbstractTypeSymbol implements SingletonTypeSymbol { private final String typeName; - private final BLangLiteral shape; + private final BType broadType; private TypeSymbol originalType; - public BallerinaSingletonTypeSymbol(CompilerContext context, BLangLiteral shape, BType bType) { - super(context, TypeDescKind.SINGLETON, bType); - - if (shape.value == null && shape.originalValue == null) { - this.typeName = ""; - // Special case handling for `()` since in BLangLiteral, `null` is used to represent nil. - } else if (shape.getBType().tag == TypeTags.NIL) { - this.typeName = "()"; - // Special case handling for string type. - } else if (shape.getBType().tag == TypeTags.STRING) { - this.typeName = "\"" + shape + "\""; + public BallerinaSingletonTypeSymbol(CompilerContext context, BType broadType, String value, BType bFiniteType) { + super(context, TypeDescKind.SINGLETON, bFiniteType); + if (TypeTags.STRING == broadType.tag) { + this.typeName = "\"" + value + "\""; } else { - this.typeName = shape.toString(); + this.typeName = value; } - this.shape = shape; + this.broadType = broadType; } @Override public List langLibMethods() { if (this.langLibFunctions == null) { LangLibrary langLibrary = LangLibrary.getInstance(this.context); - BFiniteType internalType = (BFiniteType) this.getBType(); - BType valueType = internalType.getValueSpace().iterator().next().getBType(); - List functions = langLibrary.getMethods(valueType); - this.langLibFunctions = filterLangLibMethods(functions, valueType); + List functions = langLibrary.getMethods(broadType); + this.langLibFunctions = filterLangLibMethods(functions, broadType); } return this.langLibFunctions; @@ -95,8 +84,7 @@ public TypeSymbol originalType() { } TypesFactory typesFactory = TypesFactory.getInstance(this.context); - originalType = typesFactory.getTypeDescriptor(shape.getBType()); - + originalType = typesFactory.getTypeDescriptor(broadType); return originalType; } } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaUnionTypeSymbol.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaUnionTypeSymbol.java index 101f370bd464..85714fa67c5d 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaUnionTypeSymbol.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/BallerinaUnionTypeSymbol.java @@ -16,35 +16,51 @@ */ package io.ballerina.compiler.api.impl.symbols; -import io.ballerina.compiler.api.ModuleID; import io.ballerina.compiler.api.SymbolTransformer; import io.ballerina.compiler.api.SymbolVisitor; import io.ballerina.compiler.api.symbols.TypeDescKind; import io.ballerina.compiler.api.symbols.TypeSymbol; import io.ballerina.compiler.api.symbols.UnionTypeSymbol; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.util.CompilerContext; +import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.StringJoiner; import java.util.regex.Pattern; import static io.ballerina.compiler.api.symbols.TypeDescKind.FUNCTION; import static io.ballerina.compiler.api.symbols.TypeDescKind.INTERSECTION; import static io.ballerina.compiler.api.symbols.TypeDescKind.NIL; +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.SemTypes.isSubtypeSimple; /** * Represents an union type descriptor. @@ -60,13 +76,16 @@ public class BallerinaUnionTypeSymbol extends AbstractTypeSymbol implements Unio private List memberTypes; private List originalMemberTypes; private String signature; + private SymbolTable symTable; public BallerinaUnionTypeSymbol(CompilerContext context, BUnionType unionType) { super(context, TypeDescKind.UNION, unionType); + this.symTable = SymbolTable.getInstance(context); } public BallerinaUnionTypeSymbol(CompilerContext context, BFiniteType finiteType) { super(context, TypeDescKind.UNION, finiteType); + this.symTable = SymbolTable.getInstance(context); } @Override @@ -82,22 +101,10 @@ public List memberTypeDescriptors() { members.add(typesFactory.getTypeDescriptor(memberType)); continue; } - - BFiniteType finiteType = (BFiniteType) memberType; - for (BLangExpression value : finiteType.getValueSpace()) { - ModuleID moduleID = getModule().isPresent() ? getModule().get().id() : null; - BFiniteType bFiniteType = new BFiniteType(value.getBType().tsymbol, Set.of(value)); - members.add(new BallerinaSingletonTypeSymbol(this.context, (BLangLiteral) value, - bFiniteType)); - } + updateMembersForBFiniteType(members, (BFiniteType) memberType); } } else { - for (BLangExpression value : ((BFiniteType) this.getBType()).getValueSpace()) { - ModuleID moduleID = getModule().isPresent() ? getModule().get().id() : null; - BFiniteType bFiniteType = new BFiniteType(value.getBType().tsymbol, Set.of(value)); - members.add(new BallerinaSingletonTypeSymbol(this.context, (BLangLiteral) value, - bFiniteType)); - } + updateMembersForBFiniteType(members, (BFiniteType) this.getBType()); } this.memberTypes = Collections.unmodifiableList(members); @@ -106,6 +113,46 @@ public List memberTypeDescriptors() { return this.memberTypes; } + @SuppressWarnings("OptionalGetWithoutIsPresent") // xxxSubtypeSingleValue() are guaranteed to have a value + private void updateMembersForBFiniteType(List members, BFiniteType bFiniteType) { + for (SemNamedType semNamedType : bFiniteType.valueSpace) { + SemType s = semNamedType.semType(); + BFiniteType ft = BFiniteType.newSingletonBFiniteType(null, s); + if (PredefinedType.NIL.equals(s)) { + members.add(new BallerinaSingletonTypeSymbol(context, symTable.nilType, Names.NIL_VALUE.value, ft)); + continue; + } + + ComplexSemType cs = (ComplexSemType) s; + BType broadType; + String value; + if (isSubtypeSimple(s, PredefinedType.BOOLEAN)) { + broadType = symTable.booleanType; + boolean boolVal = BooleanSubtype.booleanSubtypeSingleValue(getComplexSubtypeData(cs, BT_BOOLEAN)).get(); + value = boolVal ? Names.TRUE.value : Names.FALSE.value; + } else if (isSubtypeSimple(s, PredefinedType.INT)) { + broadType = symTable.intType; + long longVal = IntSubtype.intSubtypeSingleValue(getComplexSubtypeData(cs, BT_INT)).get(); + value = Long.toString(longVal); + } else if (isSubtypeSimple(s, PredefinedType.FLOAT)) { + broadType = symTable.floatType; + double doubleVal = FloatSubtype.floatSubtypeSingleValue(getComplexSubtypeData(cs, BT_FLOAT)).get(); + value = Double.toString(doubleVal); + } else if (isSubtypeSimple(s, PredefinedType.DECIMAL)) { + broadType = symTable.decimalType; + BigDecimal bVal = DecimalSubtype.decimalSubtypeSingleValue(getComplexSubtypeData(cs, BT_DECIMAL)).get(); + value = bVal.toPlainString(); + } else if (isSubtypeSimple(s, PredefinedType.STRING)) { + broadType = symTable.stringType; + value = StringSubtype.stringSubtypeSingleValue(getComplexSubtypeData(cs, BT_STRING)).get(); + } else { + throw new IllegalStateException("Unexpected value space type: " + s); + } + + members.add(new BallerinaSingletonTypeSymbol(context, broadType, value, ft)); + } + } + @Override public List userSpecifiedMemberTypes() { if (this.originalMemberTypes == null) { @@ -118,11 +165,7 @@ public List userSpecifiedMemberTypes() { members.add(typesFactory.getTypeDescriptor(memberType)); } } else { - for (BLangExpression value : ((BFiniteType) this.getBType()).getValueSpace()) { - ModuleID moduleID = getModule().isPresent() ? getModule().get().id() : null; - members.add(new BallerinaSingletonTypeSymbol(this.context, (BLangLiteral) value, - value.getBType())); - } + updateMembersForBFiniteType(members, (BFiniteType) this.getBType()); } this.originalMemberTypes = Collections.unmodifiableList(members); @@ -164,7 +207,7 @@ private String getSignatureForUnion(BType type) { if (unionType.isCyclic && (unionType.tsymbol != null) && !unionType.tsymbol.getName().getValue().isEmpty()) { String typeStr; typeStr = unionType.tsymbol.getName().getValue(); - if (Symbols.isFlagOn(unionType.flags, Flags.TYPE_PARAM) && pCloneableType.matcher(typeStr).matches()) { + if (Symbols.isFlagOn(unionType.getFlags(), Flags.TYPE_PARAM) && pCloneableType.matcher(typeStr).matches()) { typeStr = CLONEABLE; } return typeStr; @@ -225,7 +268,7 @@ private boolean containsTwoElements(List types) { if (types.size() == 2) { for (TypeSymbol type : types) { BType internalType = ((AbstractTypeSymbol) type).getBType(); - if (internalType.tag == TypeTags.FINITE && ((BFiniteType) internalType).getValueSpace().size() > 1) { + if (internalType.tag == TypeTags.FINITE && Core.singleShape(internalType.semType()).isEmpty()) { return false; } } diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/TypesFactory.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/TypesFactory.java index 3c0705554eda..129b085a4ce3 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/TypesFactory.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/symbols/TypesFactory.java @@ -22,9 +22,13 @@ import io.ballerina.compiler.api.symbols.TypeDescKind; import io.ballerina.compiler.api.symbols.TypeSymbol; import io.ballerina.compiler.api.symbols.XMLTypeSymbol; +import io.ballerina.types.Core; +import io.ballerina.types.Value; import org.ballerinalang.model.symbols.SymbolKind; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.parser.BLangAnonymousModelHelper; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; +import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BClassSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; @@ -44,7 +48,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BReadonlyType; @@ -59,13 +62,12 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLSubType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.util.Flags; -import java.util.Set; +import java.util.Objects; +import java.util.Optional; import static org.ballerinalang.model.types.TypeKind.PARAMETERIZED; import static org.wso2.ballerinalang.compiler.util.TypeTags.ANY; @@ -112,6 +114,7 @@ public class TypesFactory { private final CompilerContext context; private final SymbolFactory symbolFactory; private final BLangAnonymousModelHelper anonymousModelHelper; + private SymbolTable symTable; private TypesFactory(CompilerContext context) { context.put(TYPES_FACTORY_KEY, this); @@ -119,6 +122,7 @@ private TypesFactory(CompilerContext context) { this.context = context; this.symbolFactory = SymbolFactory.getInstance(context); this.anonymousModelHelper = BLangAnonymousModelHelper.getInstance(context); + this.symTable = SymbolTable.getInstance(context); } public static TypesFactory getInstance(CompilerContext context) { @@ -231,16 +235,16 @@ private TypeSymbol createTypeDescriptor(BType bType, BTypeSymbol tSymbol) { case TYPEDESC: return new BallerinaTypeDescTypeSymbol(this.context, (BTypedescType) bType); case NIL: - return new BallerinaNilTypeSymbol(this.context, (BNilType) bType); + return new BallerinaNilTypeSymbol(this.context, bType); case FINITE: BFiniteType finiteType = (BFiniteType) bType; - Set valueSpace = finiteType.getValueSpace(); - - if (valueSpace.size() == 1) { - BLangExpression shape = valueSpace.iterator().next(); - return new BallerinaSingletonTypeSymbol(this.context, (BLangLiteral) shape, bType); + Optional value = Core.singleShape(finiteType.semType()); + if (value.isPresent()) { + BType broadType = SemTypeHelper.broadTypes(finiteType, symTable).iterator() + .next(); + String valueString = Objects.toString(value.get().value, "()"); + return new BallerinaSingletonTypeSymbol(this.context, broadType, valueString, bType); } - return new BallerinaUnionTypeSymbol(this.context, finiteType); case FUNCTION: return new BallerinaFunctionTypeSymbol(this.context, (BInvokableTypeSymbol) tSymbol, bType); diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaArrayTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaArrayTypeBuilder.java index 72702a3e9105..eb01b5ba9838 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaArrayTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaArrayTypeBuilder.java @@ -86,7 +86,7 @@ public ArrayTypeSymbol build() { BTypeSymbol arrayTSymbol = Symbols.createTypeSymbol(SymTag.ARRAY_TYPE, Flags.PUBLIC, Names.EMPTY, symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BArrayType arrayType = new BArrayType(getBType(type), arrayTSymbol, size, state); + BArrayType arrayType = new BArrayType(symTable.typeEnv(), getBType(type), arrayTSymbol, size, state); arrayTSymbol.type = arrayType; ArrayTypeSymbol arrayTypeSymbol = (ArrayTypeSymbol) typesFactory.getTypeDescriptor(arrayType); this.size = -1; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaErrorTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaErrorTypeBuilder.java index 179cb64da617..04d1ffbd17af 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaErrorTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaErrorTypeBuilder.java @@ -71,7 +71,7 @@ public ErrorTypeSymbol build() { symTable.rootPkgSymbol.pkgID, symTable.errorType, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BErrorType errorType = new BErrorType(errorTSymbol, getBType(typeParam)); + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorTSymbol, getBType(typeParam)); errorTSymbol.type = errorType; ErrorTypeSymbol errorTypeSymbol = (ErrorTypeSymbol) typesFactory.getTypeDescriptor(errorType); this.typeParam = null; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFunctionTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFunctionTypeBuilder.java index 178519d7121a..c64d8c6cd2cc 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFunctionTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFunctionTypeBuilder.java @@ -107,7 +107,8 @@ public FunctionTypeSymbol build() { tSymbol.returnType = returnType; tSymbol.params = getParamSymbols(parameterSymbols); tSymbol.restParam = getRestParamSymbol(restParam, restType); - BInvokableType bInvokableType = new BInvokableType(paramTypes, restType, returnType, tSymbol); + BInvokableType bInvokableType = + new BInvokableType(symTable.typeEnv(), paramTypes, restType, returnType, tSymbol); FunctionTypeSymbol functionTypeSymbol = (FunctionTypeSymbol) typesFactory.getTypeDescriptor(bInvokableType); parameterSymbols.clear(); restParam = null; @@ -139,7 +140,7 @@ private BType getRestType(ParameterSymbol restParam) { BTypeSymbol restArraySymbol = Symbols.createTypeSymbol(SymTag.ARRAY_TYPE, Flags.PUBLIC, Names.EMPTY, symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BArrayType restArrayType = new BArrayType(bType, restArraySymbol, -1, BArrayState.OPEN); + BArrayType restArrayType = new BArrayType(symTable.typeEnv(), bType, restArraySymbol, -1, BArrayState.OPEN); restArraySymbol.type = restArrayType; return restArrayType; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFutureTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFutureTypeBuilder.java index 073cf34788a8..af0fcab472d5 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFutureTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaFutureTypeBuilder.java @@ -31,7 +31,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.Names; -import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; import static org.ballerinalang.model.symbols.SymbolOrigin.COMPILED_SOURCE; @@ -63,7 +62,7 @@ public FutureTypeSymbol build() { BTypeSymbol futureTSymbol = Symbols.createTypeSymbol(SymTag.TYPE, Flags.PUBLIC, Names.EMPTY, symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BFutureType futureType = new BFutureType(TypeTags.FUTURE, getBType(typeParam), futureTSymbol); + BFutureType futureType = new BFutureType(symTable.typeEnv(), getBType(typeParam), futureTSymbol); futureTSymbol.type = futureType; FutureTypeSymbol futureTypeSymbol = (FutureTypeSymbol) typesFactory.getTypeDescriptor(futureType); this.typeParam = null; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaMapTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaMapTypeBuilder.java index a97976f1a1e8..f0df782d7a48 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaMapTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaMapTypeBuilder.java @@ -63,7 +63,7 @@ public MapTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, symTable.rootPkgSymbol.origin); - BMapType mapType = new BMapType(TypeTags.MAP, getBType(typeParam), mapTSymbol); + BMapType mapType = new BMapType(symTable.typeEnv(), TypeTags.MAP, getBType(typeParam), mapTSymbol); mapTSymbol.type = mapType; MapTypeSymbol mapTypeSymbol = (MapTypeSymbol) typesFactory.getTypeDescriptor(mapType); this.typeParam = null; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaObjectTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaObjectTypeBuilder.java index ed7387eab7c7..6dc6cfbce892 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaObjectTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaObjectTypeBuilder.java @@ -132,7 +132,7 @@ public ObjectTypeSymbol build() { BObjectTypeSymbol objectSymbol = Symbols.createObjectSymbol(Flags.asMask(flags), Names.EMPTY, symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol.owner, symTable.builtinPos, COMPILED_SOURCE); - BObjectType objectType = new BObjectType(objectSymbol, typeFlags); + BObjectType objectType = new BObjectType(symTable.typeEnv(), objectSymbol, typeFlags); objectType.fields = getObjectFields(objectFieldList, objectSymbol); objectType.typeInclusions.addAll(getTypeInclusions(typeInclusions)); objectSymbol.type = objectType; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaRecordTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaRecordTypeBuilder.java index 33c88117390f..cb7d7d80e089 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaRecordTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaRecordTypeBuilder.java @@ -95,7 +95,7 @@ public RecordTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, symTable.rootPkgSymbol.origin); - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordSymbol); recordSymbol.type = recordType; recordType.typeInclusions = getTypeInclusions(typeInclusions); if (restField == null) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaSingletonTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaSingletonTypeBuilder.java index 148ebbed813b..fc0ab4dd8cbb 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaSingletonTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaSingletonTypeBuilder.java @@ -23,6 +23,7 @@ import io.ballerina.compiler.api.impl.symbols.TypesFactory; import io.ballerina.compiler.api.symbols.SingletonTypeSymbol; import io.ballerina.compiler.api.symbols.TypeSymbol; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.SymTag; @@ -34,8 +35,6 @@ import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.util.Flags; -import java.util.Set; - import static org.ballerinalang.model.symbols.SymbolOrigin.COMPILED_SOURCE; /** @@ -77,7 +76,8 @@ public SingletonTypeSymbol build() { Names.fromString(value.toString()), symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol, Set.of(valueLiteral)); + BFiniteType finiteType = BFiniteType.newSingletonBFiniteType(finiteTypeSymbol, + SemTypeHelper.resolveSingletonType(valueLiteral)); finiteTypeSymbol.type = finiteType; SingletonTypeSymbol singletonTypeSymbol = (SingletonTypeSymbol) typesFactory.getTypeDescriptor(finiteType, finiteTypeSymbol, true); diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaStreamTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaStreamTypeBuilder.java index e71fc54136c9..2c06b0e2876a 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaStreamTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaStreamTypeBuilder.java @@ -69,7 +69,7 @@ public StreamTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, symTable.rootPkgSymbol.origin); - BStreamType streamType = new BStreamType(TypeTags.STREAM, getValueBType(this.valueType), + BStreamType streamType = new BStreamType(symTable.typeEnv(), TypeTags.STREAM, getValueBType(this.valueType), getCompletionBType(this.completionType), streamSymbol); streamSymbol.type = streamType; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTableTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTableTypeBuilder.java index 18af7c102770..a749896d70ae 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTableTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTableTypeBuilder.java @@ -43,7 +43,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.Names; -import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; import java.util.ArrayList; @@ -105,7 +104,7 @@ public TableTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, symTable.rootPkgSymbol.origin); - BTableType tableType = new BTableType(TypeTags.TABLE, rowBType, tableSymbol); + BTableType tableType = new BTableType(symTable.typeEnv(), rowBType, tableSymbol); tableSymbol.type = tableType; if (!keyTypes.isEmpty()) { tableType.keyTypeConstraint = getKeyConstraintBType(keyTypes, rowType); @@ -147,7 +146,7 @@ private boolean isReadOnlyField(RecordFieldSymbol recordField) { if (typeDescriptor instanceof AbstractTypeSymbol abstractTypeSymbol) { BType bType = abstractTypeSymbol.getBType(); - return Symbols.isFlagOn(bType.flags, Flags.READONLY); + return Symbols.isFlagOn(bType.getFlags(), Flags.READONLY); } return false; @@ -165,7 +164,7 @@ private BType getKeyConstraintBType(List keyTypes, TypeSymbol rowTyp tupleMembers.add(new BTupleMember(constraintType, varSymbol)); } - return new BTupleType(tupleMembers); + return new BTupleType(symTable.typeEnv(), tupleMembers); } private BType checkKeyConstraintBType(TypeSymbol keyType, TypeSymbol rowType) { @@ -193,7 +192,7 @@ private BType checkKeyConstraintBType(TypeSymbol keyType, TypeSymbol rowType) { private boolean isValidKeyConstraintType(TypeSymbol fieldType, TypeSymbol keyType) { if ((fieldType.typeKind() == keyType.typeKind() || keyType.subtypeOf(fieldType)) && fieldType instanceof AbstractTypeSymbol abstractTypeSymbol) { - return Symbols.isFlagOn(abstractTypeSymbol.getBType().flags, Flags.READONLY); + return Symbols.isFlagOn(abstractTypeSymbol.getBType().getFlags(), Flags.READONLY); } return false; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTupleTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTupleTypeBuilder.java index 9b401688b982..c4dbda213ef4 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTupleTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTupleTypeBuilder.java @@ -81,7 +81,7 @@ public TupleTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, SymbolOrigin.COMPILED_SOURCE); - BTupleType tupleType = new BTupleType(tupleSymbol, memberTypes); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), tupleSymbol, memberTypes); tupleSymbol.type = tupleType; BType restBType = getRestType(restType); if (restBType != null) { diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTypeDescTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTypeDescTypeBuilder.java index d387090e8562..df8a1f09f4c3 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTypeDescTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaTypeDescTypeBuilder.java @@ -62,7 +62,7 @@ public TypeDescTypeSymbol build() { symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, SymbolOrigin.COMPILED_SOURCE); - BTypedescType typedescType = new BTypedescType(getBType(typeParam), typedescSymbol); + BTypedescType typedescType = new BTypedescType(symTable.typeEnv(), getBType(typeParam), typedescSymbol); typedescSymbol.type = typedescType; TypeDescTypeSymbol typeDescTypeSymbol = (TypeDescTypeSymbol) typesFactory.getTypeDescriptor(typedescType); this.typeParam = null; diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaUnionTypeBuilder.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaUnionTypeBuilder.java index fd0d90aec70a..a9572608685f 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaUnionTypeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/type/builders/BallerinaUnionTypeBuilder.java @@ -77,7 +77,7 @@ public TypeBuilder.UNION withMemberTypes(TypeSymbol... memberTypes) { public UnionTypeSymbol build() { BTypeSymbol unionSymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, Flags.PUBLIC, Names.EMPTY, symTable.rootPkgSymbol.pkgID, null, symTable.rootPkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - BUnionType unionType = BUnionType.create(unionSymbol, getMemberBTypes()); + BUnionType unionType = BUnionType.create(symTable.typeEnv(), unionSymbol, getMemberBTypes()); UnionTypeSymbol unionTypeSymbol = (UnionTypeSymbol) typesFactory.getTypeDescriptor(unionType); memberTypes.clear(); diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ModuleContext.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ModuleContext.java index de2ec9c100ab..1bb02b26d59e 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ModuleContext.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/ModuleContext.java @@ -35,6 +35,8 @@ import org.wso2.ballerinalang.compiler.bir.writer.BIRBinaryWriter; import org.wso2.ballerinalang.compiler.diagnostic.BLangDiagnosticLocation; import org.wso2.ballerinalang.compiler.semantics.analyzer.SymbolEnter; +import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; +import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BPackageSymbol; import org.wso2.ballerinalang.compiler.tree.BLangPackage; import org.wso2.ballerinalang.compiler.tree.BLangTestablePackage; @@ -244,7 +246,8 @@ List diagnostics() { } private void parseTestSources(BLangPackage pkgNode, PackageID pkgId, CompilerContext compilerContext) { - BLangTestablePackage testablePkg = TreeBuilder.createTestablePackageNode(); + Types types = Types.getInstance(compilerContext); + BLangTestablePackage testablePkg = TreeBuilder.createTestablePackageNode(types.typeEnv()); // TODO Not sure why we need to do this. It is there in the current implementation testablePkg.packageID = pkgId; testablePkg.flagSet.add(Flag.TESTABLE); @@ -375,7 +378,8 @@ static void compileInternal(ModuleContext moduleContext, CompilerContext compile SymbolEnter symbolEnter = SymbolEnter.getInstance(compilerContext); CompilerPhaseRunner compilerPhaseRunner = CompilerPhaseRunner.getInstance(compilerContext); - BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(); + Types types = Types.getInstance(compilerContext); + BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(types.typeEnv()); pkgNode.moduleContextDataHolder = new ModuleContextDataHolder( moduleContext.isExported(), moduleContext.descriptor(), @@ -490,11 +494,13 @@ private static ByteArrayOutputStream generateBIR(ModuleContext moduleContext, Co } // Can we improve this logic ByteArrayOutputStream birContent = new ByteArrayOutputStream(); + SymbolTable symTable = SymbolTable.getInstance(compilerContext); try { CompiledBinaryFile.BIRPackageFile birPackageFile = moduleContext.bLangPackage.symbol.birPackageFile; if (birPackageFile == null) { birPackageFile = new CompiledBinaryFile - .BIRPackageFile(new BIRBinaryWriter(moduleContext.bLangPackage.symbol.bir).serialize()); + .BIRPackageFile( + new BIRBinaryWriter(moduleContext.bLangPackage.symbol.bir, symTable.typeEnv()).serialize()); moduleContext.bLangPackage.symbol.birPackageFile = birPackageFile; } byte[] pkgBirBinaryContent = PackageFileWriter.writePackage(birPackageFile); diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java index 17cdd218c171..668b0989786f 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/projects/internal/configschema/TypeConverter.java @@ -19,6 +19,14 @@ import com.google.gson.JsonArray; import com.google.gson.JsonObject; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; @@ -32,16 +40,23 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeReferenceType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangNumericLiteral; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; +import java.math.BigDecimal; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.SemTypes.isSubtypeSimple; +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; import static org.wso2.ballerinalang.compiler.util.TypeTags.BOOLEAN; import static org.wso2.ballerinalang.compiler.util.TypeTags.BYTE; import static org.wso2.ballerinalang.compiler.util.TypeTags.DECIMAL; @@ -97,7 +112,7 @@ JsonObject getType(BType type) { typeNode.addProperty(TYPE, typeVal); return typeNode; } - + VisitedType visitedType = getVisitedType(effectiveType.toString()); if (visitedType != null) { if (visitedType.isCompleted()) { @@ -267,24 +282,33 @@ private void updateUnionMembers(LinkedHashSet members, JsonArray memberAr * @param enumArray JSON array to add the enum values * @param finiteType BFiniteType to retrieve enum values from */ + @SuppressWarnings("OptionalGetWithoutIsPresent") // xxxSubtypeSingleValue() are guaranteed to have a value private static void getEnumArray(JsonArray enumArray, BFiniteType finiteType) { - Object[] values = finiteType.getValueSpace().toArray(); - for (Object finiteValue : values) { - if (finiteValue instanceof BLangNumericLiteral numericLiteral) { - BType bType = numericLiteral.getBType(); - // In the BLangNumericLiteral the integer typed values are represented as numeric values - // while the decimal values are represented as String - Object value = numericLiteral.getValue(); - if (TypeTags.isIntegerTypeTag(bType.tag)) { - // Any integer can be considered as a long and added as a numeric value to the enum array - if (value instanceof Long l) { - enumArray.add(l); - } - } else { - enumArray.add(Double.parseDouble(value.toString())); - } - } else if (finiteValue instanceof BLangLiteral bLangLiteral) { - enumArray.add(bLangLiteral.getValue().toString()); + for (SemNamedType semNamedType : finiteType.valueSpace) { + SemType s = semNamedType.semType(); + if (PredefinedType.NIL.equals(s)) { + enumArray.add(Names.NIL_VALUE.value); + continue; + } + + ComplexSemType cs = (ComplexSemType) s; + if (isSubtypeSimple(s, PredefinedType.BOOLEAN)) { + boolean boolVal = BooleanSubtype.booleanSubtypeSingleValue(getComplexSubtypeData(cs, BT_BOOLEAN)).get(); + enumArray.add(boolVal ? Names.TRUE.value : Names.FALSE.value); + } else if (isSubtypeSimple(s, PredefinedType.INT)) { + long longVal = IntSubtype.intSubtypeSingleValue(getComplexSubtypeData(cs, BT_INT)).get(); + enumArray.add(longVal); + } else if (isSubtypeSimple(s, PredefinedType.FLOAT)) { + double doubleVal = FloatSubtype.floatSubtypeSingleValue(getComplexSubtypeData(cs, BT_FLOAT)).get(); + enumArray.add(doubleVal); + } else if (isSubtypeSimple(s, PredefinedType.DECIMAL)) { + BigDecimal bVal = DecimalSubtype.decimalSubtypeSingleValue(getComplexSubtypeData(cs, BT_DECIMAL)).get(); + enumArray.add(bVal.toString()); + } else if (isSubtypeSimple(s, PredefinedType.STRING)) { + String stringVal = StringSubtype.stringSubtypeSingleValue(getComplexSubtypeData(cs, BT_STRING)).get(); + enumArray.add(stringVal); + } else { + throw new IllegalStateException("Unexpected value space type: " + s); } } } diff --git a/compiler/ballerina-lang/src/main/java/module-info.java b/compiler/ballerina-lang/src/main/java/module-info.java index bf773acd7296..320ee6cc2817 100644 --- a/compiler/ballerina-lang/src/main/java/module-info.java +++ b/compiler/ballerina-lang/src/main/java/module-info.java @@ -16,6 +16,7 @@ requires org.apache.commons.io; requires io.ballerina.toml; requires io.ballerina.central.client; + requires io.ballerina.semtype; requires io.ballerina.identifier; requires java.semver; requires maven.resolver; diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java index 7e269184fa01..891927afecda 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/TreeBuilder.java @@ -17,6 +17,7 @@ */ package org.ballerinalang.model; +import io.ballerina.types.Env; import org.ballerinalang.model.clauses.CollectClauseNode; import org.ballerinalang.model.clauses.DoClauseNode; import org.ballerinalang.model.clauses.GroupByClauseNode; @@ -395,12 +396,12 @@ public static CompilationUnitNode createCompilationUnit() { return new BLangCompilationUnit(); } - public static PackageNode createPackageNode() { - return new BLangPackage(); + public static PackageNode createPackageNode(Env typeEnv) { + return new BLangPackage(typeEnv); } - public static BLangTestablePackage createTestablePackageNode() { - return new BLangTestablePackage(); + public static BLangTestablePackage createTestablePackageNode(Env typeEnv) { + return new BLangTestablePackage(typeEnv); } public static IdentifierNode createIdentifierNode() { diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConnectorType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConnectorType.java deleted file mode 100644 index c6f9a6e9212b..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConnectorType.java +++ /dev/null @@ -1,26 +0,0 @@ -/* -* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.ballerinalang.model.types; - -/** - * {@code ConnectorType} represents the type of a connector in Ballerina. - * - * @since 0.94 - */ -public interface ConnectorType extends ReferenceType { -} diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConstrainedType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConstrainedType.java index 394001fbf8cc..8cd4c1a858d3 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConstrainedType.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ConstrainedType.java @@ -20,7 +20,7 @@ /** * @since 0.94 */ -public interface ConstrainedType extends ReferenceType { +public interface ConstrainedType { Type getConstraint(); } diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/FiniteType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/FiniteType.java deleted file mode 100644 index 165b6832a561..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/FiniteType.java +++ /dev/null @@ -1,32 +0,0 @@ -/* -* Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.ballerinalang.model.types; - -import org.ballerinalang.model.tree.expressions.ExpressionNode; - -import java.util.Set; - -/** - * {@code FiniteType} represents the finite type interface. - * - */ -public interface FiniteType extends ReferenceType { - - Set getValueSpace(); - -} diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NilType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NilType.java deleted file mode 100644 index 6f9667927726..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NilType.java +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.ballerinalang.model.types; - -/** - * {@code NilType} represents the singleton type returns by functions with no declared value. - * The value of the {@code NilType} is written as '()' - * - * @since 0.970.0 - */ -public interface NilType extends ReferenceType { -} diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NullType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NullType.java deleted file mode 100644 index 144a5036682d..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/NullType.java +++ /dev/null @@ -1,26 +0,0 @@ -/* -* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.ballerinalang.model.types; - -/** - * {@code NullType} represents the type of the expression 'null'. - * - * @since 0.94 - */ -public interface NullType extends ReferenceType { -} diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ReferenceType.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ReferenceType.java deleted file mode 100644 index ebe029307e5a..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/model/types/ReferenceType.java +++ /dev/null @@ -1,27 +0,0 @@ -/* -* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.ballerinalang.model.types; - -/** - * {@code ReferenceType} represents a reference type in Ballerina. - * These includes structs, connectors, array types, xml, json etc. - * - * @since 0.94 - */ -public interface ReferenceType extends Type { -} diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java index 63b9648cd3bd..6f1037b9bb94 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java @@ -778,7 +778,7 @@ public enum DiagnosticErrorCode implements DiagnosticCode { UNDEFINED_RESOURCE("BCE4028", "undefined.resource"), UNDEFINED_RESOURCE_METHOD("BCE4029", "undefined.resource.method"), AMBIGUOUS_RESOURCE_ACCESS_NOT_YET_SUPPORTED("BCE4030", "ambiguous.resource.access.not.yet.supported"), - UNSUPPORTED_COMPUTED_RESOURCE_ACCESS_PATH_SEGMENT_TYPE("BCE4031", + UNSUPPORTED_COMPUTED_RESOURCE_ACCESS_PATH_SEGMENT_TYPE("BCE4031", "unsupported.computed.resource.access.path.segment.type"), UNSUPPORTED_RESOURCE_ACCESS_REST_SEGMENT_TYPE("BCE4032", "unsupported.resource.access.rest.segment.type"), INVALID_RESOURCE_METHOD_RETURN_TYPE("BCE4033", "invalid.resource.method.return.type"), diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/BIRPackageSymbolEnter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/BIRPackageSymbolEnter.java index 97e943698e19..044548c34fde 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/BIRPackageSymbolEnter.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/BIRPackageSymbolEnter.java @@ -18,8 +18,40 @@ package org.wso2.ballerinalang.compiler; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Atom; +import io.ballerina.types.AtomicType; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableDecimal; +import io.ballerina.types.EnumerableFloat; +import io.ballerina.types.EnumerableString; +import io.ballerina.types.Env; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.FunctionAtomicType; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.MappingAtomicType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.PredefinedTypeEnv; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.TypeAtom; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.CharStringSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.NonCharStringSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.StringSubtype; +import io.ballerina.types.subtypedata.XmlSubtype; import org.ballerinalang.compiler.BLangCompilerException; -import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.AttachPoint; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.MarkdownDocAttachment; @@ -27,7 +59,6 @@ import org.ballerinalang.model.symbols.Annotatable; import org.ballerinalang.model.symbols.SymbolKind; import org.ballerinalang.model.symbols.SymbolOrigin; -import org.ballerinalang.model.tree.NodeKind; import org.ballerinalang.model.types.ConstrainedType; import org.wso2.ballerinalang.compiler.bir.writer.CPEntry; import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.ByteCPEntry; @@ -65,6 +96,7 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.SymTag; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnnotationType; +import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; @@ -86,11 +118,8 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.semantics.model.types.TypeFlags; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.tree.BLangConstantValue; -import org.wso2.ballerinalang.compiler.tree.BLangPackage; -import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.util.BArrayState; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.ImmutableTypeCloner; @@ -105,6 +134,8 @@ import java.io.DataInputStream; import java.io.IOException; import java.io.InputStream; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.ArrayList; import java.util.Arrays; import java.util.EnumSet; @@ -116,13 +147,14 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.Consumer; +import static io.ballerina.types.PredefinedType.BDD_REC_ATOM_READONLY; import static org.ballerinalang.model.symbols.SymbolOrigin.COMPILED_SOURCE; import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL; import static org.ballerinalang.model.symbols.SymbolOrigin.toOrigin; -import static org.wso2.ballerinalang.compiler.parser.BLangAnonymousModelHelper.ANON_PREFIX; import static org.wso2.ballerinalang.compiler.semantics.model.Scope.NOT_FOUND_ENTRY; import static org.wso2.ballerinalang.util.LambdaExceptionUtils.rethrow; @@ -132,6 +164,7 @@ * @since 0.995.0 */ public class BIRPackageSymbolEnter { + private final PackageCache packageCache; private final SymbolResolver symbolResolver; private final SymbolTable symTable; @@ -144,6 +177,8 @@ public class BIRPackageSymbolEnter { private List structureTypes; // TODO find a better way private BStructureTypeSymbol currentStructure = null; private final LinkedList compositeStack = new LinkedList<>(); + private final Env typeEnv; + private AtomOffsets offsets; private static final CompilerContext.Key COMPILED_PACKAGE_SYMBOL_ENTER_KEY = new CompilerContext.Key<>(); @@ -168,6 +203,8 @@ private BIRPackageSymbolEnter(CompilerContext context) { this.names = Names.getInstance(context); this.typeParamAnalyzer = TypeParamAnalyzer.getInstance(context); this.types = Types.getInstance(context); + this.typeEnv = symTable.typeEnv(); + this.offsets = null; } public BPackageSymbol definePackage(PackageID packageId, byte[] packageBinaryContent) { @@ -231,6 +268,7 @@ private BPackageSymbol definePackage(DataInputStream dataInStream, int pkgCpInde PackageID pkgId = createPackageID(orgName, pkgName, moduleName, pkgVersion); this.env.pkgSymbol = Symbols.createPackageSymbol(pkgId, this.symTable, COMPILED_SOURCE); + this.offsets = AtomOffsets.from(typeEnv); // TODO Validate this pkdID with the requestedPackageID available in the env. @@ -261,6 +299,7 @@ private BPackageSymbol definePackage(DataInputStream dataInStream, int pkgCpInde populateReferencedFunctions(); this.typeReader = null; + this.offsets = null; return this.env.pkgSymbol; } @@ -606,7 +645,7 @@ private void skipPosition(DataInputStream dataInStream) throws IOException { } private void setInvokableTypeSymbol(BInvokableType invokableType) { - if (Symbols.isFlagOn(invokableType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(invokableType.getFlags(), Flags.ANY_FUNCTION)) { return; } BInvokableTypeSymbol tsymbol = (BInvokableTypeSymbol) invokableType.tsymbol; @@ -932,7 +971,7 @@ private void setParamSymbols(BInvokableSymbol invokableSymbol, DataInputStream d defineAnnotAttachmentSymbols(dataInStream, restParam); } - if (Symbols.isFlagOn(invokableSymbol.retType.flags, Flags.PARAMETERIZED)) { + if (Symbols.isFlagOn(invokableSymbol.retType.getFlags(), Flags.PARAMETERIZED)) { Map paramsMap = new HashMap<>(); for (BVarSymbol param : invokableSymbol.params) { if (paramsMap.put(param.getName(), param) != null) { @@ -1034,7 +1073,7 @@ private void populateParameterizedType(BType type, final Map p break; case TypeTags.INVOKABLE: BInvokableType invokableType = (BInvokableType) type; - if (Symbols.isFlagOn(invokableType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(invokableType.getFlags(), Flags.ANY_FUNCTION)) { break; } for (BType t : invokableType.paramTypes) { @@ -1132,6 +1171,7 @@ private static class UnresolvedType { private class BIRTypeReader { private final DataInputStream inputStream; + private final PredefinedTypeEnv predefinedTypeEnv = PredefinedTypeEnv.getInstance(); public BIRTypeReader(DataInputStream inputStream) { this.inputStream = inputStream; @@ -1220,8 +1260,6 @@ public BType readType(int cpI) throws IOException { Name name = Names.fromString(getStringCPEntryValue(inputStream)); long flags = inputStream.readLong(); - // Read the type flags to identify if type reference types are nullable. - int typeFlags = inputStream.readInt(); switch (tag) { case TypeTags.INT: return typeParamAnalyzer.getNominalType(symTable.intType, name, flags); @@ -1242,7 +1280,7 @@ public BType readType(int cpI) throws IOException { BType constraintType = readTypeFromCp(); BXMLType mutableXmlType = new BXMLType(constraintType, symTable.xmlType.tsymbol); if (Symbols.isFlagOn(flags, Flags.PARAMETERIZED)) { - mutableXmlType.flags |= Flags.PARAMETERIZED; + mutableXmlType.addFlags(Flags.PARAMETERIZED); } return isImmutable(flags) ? getEffectiveImmutableType(mutableXmlType) : mutableXmlType; case TypeTags.NIL: @@ -1270,7 +1308,7 @@ public BType readType(int cpI) throws IOException { COMPILED_SOURCE); recordSymbol.scope = new Scope(recordSymbol); - BRecordType recordType = new BRecordType(recordSymbol, flags); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordSymbol, flags); recordSymbol.type = recordType; compositeStack.push(recordType); @@ -1319,11 +1357,11 @@ public BType readType(int cpI) throws IOException { } SymbolEnv pkgEnv = symTable.pkgEnvMap.get(packageCache.getSymbol(pkgId)); - return getType(recordType, pkgEnv, Names.fromString(recordName)); + return lookupSymbolInMainSpace(pkgEnv, Names.fromString(recordName)); case TypeTags.TYPEDESC: - BTypedescType typedescType = new BTypedescType(null, symTable.typeDesc.tsymbol); + BTypedescType typedescType = new BTypedescType(symTable.typeEnv(), null, symTable.typeDesc.tsymbol); typedescType.constraint = readTypeFromCp(); - typedescType.flags = flags; + typedescType.setFlags(flags); return typedescType; case TypeTags.TYPEREFDESC: int pkgIndex = inputStream.readInt(); @@ -1337,9 +1375,7 @@ public BType readType(int cpI) throws IOException { Names.fromString(typeDefName), pkg, null, pkgSymbol, symTable.builtinPos, COMPILED_SOURCE); - boolean nullable = (typeFlags & TypeFlags.NILABLE) == TypeFlags.NILABLE; - - BTypeReferenceType typeReferenceType = new BTypeReferenceType(null, typeSymbol, flags, nullable); + BTypeReferenceType typeReferenceType = new BTypeReferenceType(null, typeSymbol, flags); addShapeCP(typeReferenceType, cpI); compositeStack.push(typeReferenceType); typeReferenceType.referredType = readTypeFromCp(); @@ -1350,17 +1386,19 @@ public BType readType(int cpI) throws IOException { case TypeTags.PARAMETERIZED_TYPE: BParameterizedType type = new BParameterizedType(null, null, null, name, -1); type.paramValueType = readTypeFromCp(); - type.flags = flags; + type.setFlags(flags); type.paramIndex = inputStream.readInt(); return type; case TypeTags.STREAM: - BStreamType bStreamType = new BStreamType(TypeTags.STREAM, null, null, symTable.streamType.tsymbol); + BStreamType bStreamType = new BStreamType(symTable.typeEnv(), TypeTags.STREAM, null, null, + symTable.streamType.tsymbol); bStreamType.constraint = readTypeFromCp(); bStreamType.completionType = readTypeFromCp(); - bStreamType.flags = flags; + bStreamType.setFlags(flags); return bStreamType; case TypeTags.TABLE: - BTableType bTableType = new BTableType(TypeTags.TABLE, null, symTable.tableType.tsymbol, flags); + BTableType bTableType = new BTableType(symTable.typeEnv(), null, + symTable.tableType.tsymbol, flags); bTableType.constraint = readTypeFromCp(); boolean hasFieldNameList = inputStream.readByte() == 1; @@ -1386,16 +1424,17 @@ public BType readType(int cpI) throws IOException { } return bTableType; case TypeTags.MAP: - BMapType bMapType = new BMapType(TypeTags.MAP, null, symTable.mapType.tsymbol, flags); + BMapType bMapType = new BMapType(symTable.typeEnv(), TypeTags.MAP, null, symTable.mapType.tsymbol, + flags); bMapType.constraint = readTypeFromCp(); return bMapType; case TypeTags.INVOKABLE: - BInvokableType bInvokableType = new BInvokableType(null, null, null, null); + BInvokableType bInvokableType = new BInvokableType(typeEnv, List.of(), null, null, null); bInvokableType.tsymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, flags, env.pkgSymbol.pkgID, null, env.pkgSymbol.owner, symTable.builtinPos, COMPILED_SOURCE); - bInvokableType.flags = flags; + bInvokableType.setFlags(flags); if (inputStream.readBoolean()) { // Return if an any function return bInvokableType; @@ -1413,11 +1452,7 @@ public BType readType(int cpI) throws IOException { return setTSymbolForInvokableType(bInvokableType, bInvokableType.retType); // All the above types are branded types case TypeTags.ANY: - BType anyNominalType = typeParamAnalyzer.getNominalType(symTable.anyType, name, flags); - return isImmutable(flags) ? getEffectiveImmutableType(anyNominalType, - symTable.anyType.tsymbol.pkgID, - symTable.anyType.tsymbol.owner) : - anyNominalType; + return isImmutable(flags) ? BAnyType.newImmutableBAnyType() : new BAnyType(name, flags); case TypeTags.HANDLE: return symTable.handleType; case TypeTags.READONLY: @@ -1433,7 +1468,8 @@ public BType readType(int cpI) throws IOException { Names.EMPTY, env.pkgSymbol.pkgID, null, env.pkgSymbol.owner, symTable.builtinPos, COMPILED_SOURCE); - BArrayType bArrayType = new BArrayType(null, arrayTypeSymbol, size, BArrayState.valueOf(state), + BArrayType bArrayType = + new BArrayType(symTable.typeEnv(), null, arrayTypeSymbol, size, BArrayState.valueOf(state), flags); bArrayType.eType = readTypeFromCp(); return bArrayType; @@ -1453,13 +1489,14 @@ public BType readType(int cpI) throws IOException { null, env.pkgSymbol, symTable.builtinPos, COMPILED_SOURCE); int unionMemberCount = inputStream.readInt(); - BUnionType unionType = BUnionType.create(unionTypeSymbol, new LinkedHashSet<>(unionMemberCount)); + BUnionType unionType = + BUnionType.create(types.typeEnv(), unionTypeSymbol, new LinkedHashSet<>(unionMemberCount)); unionType.name = unionName; addShapeCP(unionType, cpI); compositeStack.push(unionType); - unionType.flags = flags; + unionType.setFlags(flags); unionType.isCyclic = isCyclic; for (int i = 0; i < unionMemberCount; i++) { unionType.add(readTypeFromCp()); @@ -1486,7 +1523,7 @@ public BType readType(int cpI) throws IOException { } else { pkgEnv = symTable.pkgEnvMap.get(packageCache.getSymbol(unionsPkgId)); if (pkgEnv != null) { - BType existingUnionType = getType(unionType, pkgEnv, unionName); + BType existingUnionType = lookupSymbolInMainSpace(pkgEnv, unionName); if (existingUnionType != symTable.noType) { return existingUnionType; } @@ -1538,13 +1575,13 @@ public BType readType(int cpI) throws IOException { errorSymbol = new BErrorTypeSymbol(SymTag.ERROR, Flags.PUBLIC, Names.EMPTY, env.pkgSymbol.pkgID, null, env.pkgSymbol, symTable.builtinPos, COMPILED_SOURCE); } - BErrorType errorType = new BErrorType(errorSymbol); + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorSymbol); addShapeCP(errorType, cpI); compositeStack.push(errorType); String errorName = getStringCPEntryValue(inputStream); BType detailsType = readTypeFromCp(); errorType.detailType = detailsType; - errorType.flags = flags; + errorType.setFlags(flags); errorSymbol.type = errorType; errorSymbol.pkgID = pkgId; errorSymbol.originalName = errorSymbol.name = Names.fromString(errorName); @@ -1583,8 +1620,8 @@ public BType readType(int cpI) throws IOException { tupleMembers.add(new BTupleMember(memberType, varSymbol)); } - BTupleType bTupleType = new BTupleType(tupleTypeSymbol, tupleMembers); - bTupleType.flags = flags; + BTupleType bTupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, tupleMembers); + bTupleType.setFlags(flags); if (inputStream.readBoolean()) { bTupleType.restType = readTypeFromCp(); @@ -1592,9 +1629,9 @@ public BType readType(int cpI) throws IOException { return bTupleType; case TypeTags.FUTURE: - BFutureType bFutureType = new BFutureType(TypeTags.FUTURE, null, symTable.futureType.tsymbol); + BFutureType bFutureType = new BFutureType(symTable.typeEnv(), null, symTable.futureType.tsymbol); bFutureType.constraint = readTypeFromCp(); - bFutureType.flags = flags; + bFutureType.setFlags(flags); return bFutureType; case TypeTags.FINITE: String finiteTypeName = getStringCPEntryValue(inputStream); @@ -1604,13 +1641,14 @@ public BType readType(int cpI) throws IOException { null, env.pkgSymbol, symTable.builtinPos, COMPILED_SOURCE); symbol.scope = new Scope(symbol); - BFiniteType finiteType = new BFiniteType(symbol); - finiteType.flags = flags; - symbol.type = finiteType; - int valueSpaceSize = inputStream.readInt(); - for (int i = 0; i < valueSpaceSize; i++) { - defineValueSpace(inputStream, finiteType, this); + int valueSpaceLength = inputStream.readInt(); + SemNamedType[] valueSpace = new SemNamedType[valueSpaceLength]; + for (int i = 0; i < valueSpaceLength; i++) { + valueSpace[i] = readSemNamedType(); } + BFiniteType finiteType = new BFiniteType(symbol, valueSpace); + finiteType.setFlags(flags); + symbol.type = finiteType; return finiteType; case TypeTags.OBJECT: pkgCpIndex = inputStream.readInt(); @@ -1633,8 +1671,8 @@ public BType readType(int cpI) throws IOException { objectSymbol.scope = new Scope(objectSymbol); BObjectType objectType; // Below is a temporary fix, need to fix this properly by using the type tag - objectType = new BObjectType(objectSymbol); - objectType.flags = flags; + objectType = new BObjectType(symTable.typeEnv(), objectSymbol); + objectType.setFlags(flags); objectSymbol.type = objectType; addShapeCP(objectType, cpI); compositeStack.push(objectType); @@ -1687,7 +1725,7 @@ public BType readType(int cpI) throws IOException { } pkgEnv = symTable.pkgEnvMap.get(packageCache.getSymbol(pkgId)); - return getType(objectType, pkgEnv, Names.fromString(objName)); + return lookupSymbolInMainSpace(pkgEnv, Names.fromString(objName)); case TypeTags.BYTE_ARRAY: // TODO fix break; @@ -1814,7 +1852,7 @@ private void populateIntersectionTypeReferencedFunctions(DataInputStream inputSt setInvokableTypeSymbol(attachedFuncType); - if (!Symbols.isFlagOn(attachedFuncType.flags, Flags.ANY_FUNCTION)) { + if (!Symbols.isFlagOn(attachedFuncType.getFlags(), Flags.ANY_FUNCTION)) { BInvokableTypeSymbol tsymbol = (BInvokableTypeSymbol) attachedFuncType.tsymbol; attachedFuncSymbol.params = tsymbol.params; attachedFuncSymbol.restParam = tsymbol.restParam; @@ -1825,45 +1863,294 @@ private void populateIntersectionTypeReferencedFunctions(DataInputStream inputSt objectSymbol.attachedFuncs.add(attachedFunction); objectSymbol.scope.define(funcName, attachedFuncSymbol); } - } - private BType getType(BType readShape, SymbolEnv pkgEnv, Name name) { - BType type = symbolResolver.lookupSymbolInMainSpace(pkgEnv, name).type; + private Optional readNullableString() throws IOException { + boolean hasNonNullString = inputStream.readBoolean(); + if (hasNonNullString) { + return Optional.of(getStringCPEntryValue(inputStream)); + } else { + return Optional.empty(); + } + } - if (type != symTable.noType && (!name.value.contains(ANON_PREFIX) || types.isSameBIRShape(readShape, type))) { - return type; + private SemNamedType readSemNamedType() throws IOException { + SemType semType = readSemType(); + Optional optName = readNullableString(); + return new SemNamedType(semType, optName); } - if (pkgEnv.node != null) { - for (BLangTypeDefinition typeDefinition : ((BLangPackage) pkgEnv.node).typeDefinitions) { - BSymbol symbol = typeDefinition.symbol; + // --------------------------------------- Read SemType ---------------------------------------------- - String typeDefName = typeDefinition.name.value; - if (typeDefName.contains(ANON_PREFIX)) { - BType anonType = symbol.type; + private SemType readSemType() throws IOException { + if (!inputStream.readBoolean()) { + return null; + } - if (types.isSameBIRShape(readShape, anonType)) { - return anonType; - } - } else if (typeDefName.equals(name.value)) { - return symbol.type; - } + if (inputStream.readBoolean()) { + int bitset = inputStream.readInt(); + return BasicTypeBitSet.from(bitset); } - } else { - for (Map.Entry value : pkgEnv.scope.entries.entrySet()) { - BSymbol symbol = value.getValue().symbol; - if (value.getKey().value.contains(ANON_PREFIX)) { - BType anonType = symbol.type; + int all = inputStream.readInt(); + int some = inputStream.readInt(); + byte subtypeDataListLength = inputStream.readByte(); + ProperSubtypeData[] subtypeList = new ProperSubtypeData[subtypeDataListLength]; + for (int i = 0; i < subtypeDataListLength; i++) { + subtypeList[i] = readProperSubtypeData(); + } + return createSemType(all, some, subtypeList); + } - if (types.isSameBIRShape(readShape, anonType)) { - return anonType; - } + private ProperSubtypeData readProperSubtypeData() throws IOException { + switch (inputStream.readByte()) { + case 1: + return readBdd(); + case 2: + return readIntSubtype(); + case 3: + return BooleanSubtype.from(inputStream.readBoolean()); + case 4: + return readFloatSubtype(); + case 5: + return readDecimalSubType(); + case 6: + return readStringSubtype(); + case 7: + return readXmlSubtype(); + default: + throw new IllegalStateException("Unexpected ProperSubtypeData kind"); + } + } + + private Bdd readBdd() throws IOException { + boolean isBddNode = inputStream.readBoolean(); + if (isBddNode) { + return readBddNode(); + } else { + boolean isAll = inputStream.readBoolean(); + return isAll ? BddAllOrNothing.bddAll() : BddAllOrNothing.bddNothing(); + } + } + + enum AtomKind { + REC, + INLINED, + TYPE + } + + private AtomKind readAtomKind() throws IOException { + return switch (inputStream.readByte()) { + case 0 -> AtomKind.REC; + case 1 -> AtomKind.INLINED; + case 2 -> AtomKind.TYPE; + default -> throw new IllegalStateException("Unexpected AtomKind kind"); + }; + } + + private BddNode readBddNode() throws IOException { + AtomKind atomKind = readAtomKind(); + Atom atom = switch (atomKind) { + case REC -> readRecAtom(); + case INLINED -> readInlinedAtom(); + case TYPE -> { + TypeAtom typeAtom = readTypeAtom(); + typeEnv.deserializeTypeAtom(typeAtom); + yield typeAtom; } + }; + + Bdd left = readBdd(); + Bdd middle = readBdd(); + Bdd right = readBdd(); + return BddNode.create(atom, left, middle, right); + } + + private Atom readInlinedAtom() throws IOException { + int recAtomIndex = inputStream.readInt(); + assert recAtomIndex != BDD_REC_ATOM_READONLY; + AtomicType atomicType = readTypeAtom().atomicType(); + Atom.Kind kind; + if (atomicType instanceof MappingAtomicType) { + recAtomIndex += offsets.mappingOffset(); + kind = Atom.Kind.MAPPING_ATOM; + } else if (atomicType instanceof ListAtomicType) { + recAtomIndex += offsets.listOffset(); + kind = Atom.Kind.LIST_ATOM; + } else if (atomicType instanceof FunctionAtomicType) { + recAtomIndex += offsets.functionOffset(); + kind = Atom.Kind.FUNCTION_ATOM; + } else { + throw new IllegalStateException("Unexpected inlined atomicType kind"); } + typeEnv.insertRecAtomAtIndex(recAtomIndex, atomicType); + RecAtom recAtom = RecAtom.createRecAtom(recAtomIndex); + recAtom.setKind(kind); + return recAtom; } - return type; + private TypeAtom readTypeAtom() throws IOException { + int index = inputStream.readInt() + offsets.atomOffset(); + AtomicType atomicType = switch (inputStream.readByte()) { + case 1 -> readMappingAtomicType(); + case 2 -> readListAtomicType(); + case 3 -> readFunctionAtomicType(); + case 4 -> readCellAtomicType(); + default -> throw new IllegalStateException("Unexpected atomicType kind"); + }; + return TypeAtom.createTypeAtom(index, atomicType); + } + + private RecAtom readRecAtom() throws IOException { + int index = inputStream.readInt(); + Optional predefinedRecAtom = predefinedTypeEnv.getPredefinedRecAtom(index); + if (predefinedRecAtom.isPresent()) { + return predefinedRecAtom.get(); + } + int kindOrdinal = inputStream.readInt(); + Atom.Kind kind = Atom.Kind.values()[kindOrdinal]; + int offset = switch (kind) { + case LIST_ATOM -> offsets.listOffset(); + case FUNCTION_ATOM -> offsets.functionOffset(); + case MAPPING_ATOM -> offsets.mappingOffset(); + case DISTINCT_ATOM -> (-offsets.distinctOffset()); + case XML_ATOM -> 0; + case CELL_ATOM -> throw new IllegalStateException("Cell atom cannot be recursive"); + }; + index += offset; + RecAtom recAtom = RecAtom.createRecAtom(index); + recAtom.setKind(kind); + return recAtom; + + } + + private CellAtomicType readCellAtomicType() throws IOException { + SemType ty = readSemType(); + byte ordinal = inputStream.readByte(); + CellAtomicType.CellMutability mut = CellAtomicType.CellMutability.values()[ordinal]; + return CellAtomicType.from(ty, mut); + } + + private MappingAtomicType readMappingAtomicType() throws IOException { + int namesLength = inputStream.readInt(); + String[] names = new String[namesLength]; + for (int i = 0; i < namesLength; i++) { + names[i] = getStringCPEntryValue(inputStream); + } + + int typesLength = inputStream.readInt(); + CellSemType[] types = new CellSemType[typesLength]; + for (int i = 0; i < typesLength; i++) { + types[i] = (CellSemType) readSemType(); + } + + CellSemType rest = (CellSemType) readSemType(); + return MappingAtomicType.from(names, types, rest); + } + + private ListAtomicType readListAtomicType() throws IOException { + int initialLength = inputStream.readInt(); + List initial = new ArrayList<>(initialLength); + for (int i = 0; i < initialLength; i++) { + initial.add((CellSemType) readSemType()); + } + + int fixedLength = inputStream.readInt(); + FixedLengthArray members = FixedLengthArray.from(initial, fixedLength); + + CellSemType rest = (CellSemType) readSemType(); + return ListAtomicType.from(members, rest); + } + + private static ComplexSemType createSemType(int all, int some, ProperSubtypeData[] subtypeList) { + if (some == PredefinedType.CELL.bitset && all == 0) { + return CellSemType.from(subtypeList); + } + return ComplexSemType.createComplexSemType(all, some, subtypeList); + } + + private FunctionAtomicType readFunctionAtomicType() throws IOException { + SemType paramType = readSemType(); + SemType retType = readSemType(); + SemType qualifiers = readSemType(); + boolean isGeneric = inputStream.readBoolean(); + return isGeneric ? FunctionAtomicType.genericFrom(paramType, retType, qualifiers) : + FunctionAtomicType.from(paramType, retType, qualifiers); + } + + private IntSubtype readIntSubtype() throws IOException { + int rangesLength = inputStream.readInt(); + Range[] ranges = new Range[rangesLength]; + for (int i = 0; i < rangesLength; i++) { + long min = inputStream.readLong(); + long max = inputStream.readLong(); + ranges[i] = new Range(min, max); + + } + return IntSubtype.createIntSubtype(ranges); + } + + private FloatSubtype readFloatSubtype() throws IOException { + boolean allowed = inputStream.readBoolean(); + int valuesLength = inputStream.readInt(); + EnumerableFloat[] values = new EnumerableFloat[valuesLength]; + for (int i = 0; i < valuesLength; i++) { + values[i] = EnumerableFloat.from(inputStream.readDouble()); + } + + return (FloatSubtype) FloatSubtype.createFloatSubtype(allowed, values); + } + + private DecimalSubtype readDecimalSubType() throws IOException { + boolean allowed = inputStream.readBoolean(); + int valuesLength = inputStream.readInt(); + EnumerableDecimal[] values = new EnumerableDecimal[valuesLength]; + for (int i = 0; i < valuesLength; i++) { + int scale = inputStream.readInt(); + int byteLen = inputStream.readInt(); + byte[] unscaleValueBytes = inputStream.readNBytes(byteLen); + BigDecimal bigDecimal = new BigDecimal(new BigInteger(unscaleValueBytes), scale); + values[i] = EnumerableDecimal.from(bigDecimal); + } + return (DecimalSubtype) DecimalSubtype.createDecimalSubtype(allowed, values); + } + + private StringSubtype readStringSubtype() throws IOException { + CharStringSubtype charStringSubtype = readCharStringSubtype(); + NonCharStringSubtype nonCharStringSubtype = readNonCharStringSubtype(); + return StringSubtype.from(charStringSubtype, nonCharStringSubtype); + } + + private CharStringSubtype readCharStringSubtype() throws IOException { + boolean allowed = inputStream.readBoolean(); + int valuesLength = inputStream.readInt(); + EnumerableCharString[] values = new EnumerableCharString[valuesLength]; + for (int i = 0; i < valuesLength; i++) { + values[i] = EnumerableCharString.from(getStringCPEntryValue(inputStream)); + } + return CharStringSubtype.from(allowed, values); + } + + private NonCharStringSubtype readNonCharStringSubtype() throws IOException { + boolean allowed = inputStream.readBoolean(); + int valuesLength = inputStream.readInt(); + EnumerableString[] values = new EnumerableString[valuesLength]; + for (int i = 0; i < valuesLength; i++) { + values[i] = EnumerableString.from(getStringCPEntryValue(inputStream)); + } + return NonCharStringSubtype.from(allowed, values); + } + + private XmlSubtype readXmlSubtype() throws IOException { + int primitives = inputStream.readInt(); + Bdd sequence = readBdd(); + return XmlSubtype.from(primitives, sequence); + } + + // --------------------------------------- End of SemType ----------------------------------------------- + } + + private BType lookupSymbolInMainSpace(SymbolEnv pkgEnv, Name name) { + return symbolResolver.lookupSymbolInMainSpace(pkgEnv, name).type; } private byte[] readDocBytes(DataInputStream inputStream) throws IOException { @@ -1886,54 +2173,6 @@ private PackageID getPackageId(int pkgCPIndex) { Names.fromString(moduleName), Names.fromString(version), null); } - private void defineValueSpace(DataInputStream dataInStream, BFiniteType finiteType, BIRTypeReader typeReader) - throws IOException { - BType valueType = typeReader.readTypeFromCp(); - - dataInStream.readInt(); // read and ignore value length - - BLangLiteral litExpr = createLiteralBasedOnType(valueType); - switch (valueType.tag) { - case TypeTags.INT: - int integerCpIndex = dataInStream.readInt(); - IntegerCPEntry integerCPEntry = (IntegerCPEntry) this.env.constantPool[integerCpIndex]; - litExpr.value = integerCPEntry.value; - break; - case TypeTags.BYTE: - int byteCpIndex = dataInStream.readInt(); - ByteCPEntry byteCPEntry = (ByteCPEntry) this.env.constantPool[byteCpIndex]; - litExpr.value = byteCPEntry.value; - break; - case TypeTags.FLOAT: - int floatCpIndex = dataInStream.readInt(); - FloatCPEntry floatCPEntry = (FloatCPEntry) this.env.constantPool[floatCpIndex]; - litExpr.value = Double.toString(floatCPEntry.value); - break; - case TypeTags.STRING: - case TypeTags.DECIMAL: - litExpr.value = getStringCPEntryValue(dataInStream); - break; - case TypeTags.BOOLEAN: - litExpr.value = dataInStream.readBoolean(); - break; - case TypeTags.NIL: - litExpr.originalValue = "null"; - break; - default: - throw new UnsupportedOperationException("finite type value is not supported for type: " + valueType); - } - - litExpr.setBType(valueType); - - finiteType.addValue(litExpr); - } - - private BLangLiteral createLiteralBasedOnType(BType valueType) { - NodeKind nodeKind = valueType.tag <= TypeTags.DECIMAL ? NodeKind.NUMERIC_LITERAL : NodeKind.LITERAL; - return nodeKind == NodeKind.LITERAL ? (BLangLiteral) TreeBuilder.createLiteralExpression() : - (BLangLiteral) TreeBuilder.createNumericLiteralExpression(); - } - private boolean isImmutable(long flags) { return Symbols.isFlagOn(flags, Flags.READONLY); } @@ -1948,4 +2187,18 @@ private BType getEffectiveImmutableType(BType type, PackageID pkgID, BSymbol own return ImmutableTypeCloner.getEffectiveImmutableType(null, types, type, pkgID, owner, symTable, null, names); } + + private record AtomOffsets(int atomOffset, int listOffset, int functionOffset, int mappingOffset, + int distinctOffset) { + + static AtomOffsets from(Env env) { + PredefinedTypeEnv predefinedTypeEnv = PredefinedTypeEnv.getInstance(); + int recAtomOffset = predefinedTypeEnv.reservedRecAtomCount(); + return new AtomOffsets(env.atomCount(), + env.recListAtomCount() - recAtomOffset, + env.recFunctionAtomCount(), + env.recMappingAtomCount() - recAtomOffset, + env.distinctAtomCount()); + } + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java index b834de6199f8..21665336fe6b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/BIRGen.java @@ -22,6 +22,7 @@ import io.ballerina.tools.diagnostics.Location; import io.ballerina.tools.text.LinePosition; import io.ballerina.tools.text.LineRange; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -59,6 +60,7 @@ import org.wso2.ballerinalang.compiler.bir.model.VarScope; import org.wso2.ballerinalang.compiler.bir.optimizer.BIROptimizer; import org.wso2.ballerinalang.compiler.diagnostic.BLangDiagnosticLocation; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BAnnotationSymbol; @@ -634,7 +636,7 @@ public void visit(BLangFunction astFunc) { // TODO: Return variable with NIL type should be written to BIR // Special %0 location for storing return values - BType retType = unifier.build(astFunc.symbol.type.getReturnType()); + BType retType = unifier.build(symTable.typeEnv(), astFunc.symbol.type.getReturnType()); birFunc.returnVariable = new BIRVariableDcl(astFunc.pos, retType, this.env.nextLocalVarId(names), VarScope.FUNCTION, VarKind.RETURN, null); birFunc.localVars.add(0, birFunc.returnVariable); @@ -2119,7 +2121,7 @@ public void visit(BLangXMLElementLiteral xmlElementLiteral) { BIRNonTerminator.NewXMLElement newXMLElement = new BIRNonTerminator.NewXMLElement(xmlElementLiteral.pos, toVarRef, startTagNameIndex, defaultNsURIVarRef, - Symbols.isFlagOn(xmlElementLiteral.getBType().flags, + Symbols.isFlagOn(xmlElementLiteral.getBType().getFlags(), Flags.READONLY)); setScopeAndEmit(newXMLElement); @@ -2185,7 +2187,7 @@ public void visit(BLangXMLCommentLiteral xmlCommentLiteral) { BIRNonTerminator.NewXMLComment newXMLComment = new BIRNonTerminator.NewXMLComment(xmlCommentLiteral.pos, toVarRef, xmlCommentIndex, - Symbols.isFlagOn(xmlCommentLiteral.getBType().flags, + Symbols.isFlagOn(xmlCommentLiteral.getBType().getFlags(), Flags.READONLY)); setScopeAndEmit(newXMLComment); this.env.targetOperand = toVarRef; @@ -2206,7 +2208,7 @@ public void visit(BLangXMLProcInsLiteral xmlProcInsLiteral) { BIRNonTerminator.NewXMLProcIns newXMLProcIns = new BIRNonTerminator.NewXMLProcIns(xmlProcInsLiteral.pos, toVarRef, dataIndex, targetIndex, - Symbols.isFlagOn(xmlProcInsLiteral.getBType().flags, + Symbols.isFlagOn(xmlProcInsLiteral.getBType().getFlags(), Flags.READONLY)); setScopeAndEmit(newXMLProcIns); this.env.targetOperand = toVarRef; @@ -2284,7 +2286,8 @@ public void visit(BLangTableConstructorExpr tableConstructorExpr) { BLangArrayLiteral dataLiteral = new BLangArrayLiteral(); dataLiteral.pos = tableConstructorExpr.pos; dataLiteral.setBType( - new BArrayType(((BTableType) Types.getImpliedType(tableConstructorExpr.getBType())).constraint)); + new BArrayType(symTable.typeEnv(), + ((BTableType) Types.getImpliedType(tableConstructorExpr.getBType())).constraint)); dataLiteral.exprs = new ArrayList<>(tableConstructorExpr.recordLiteralList); dataLiteral.accept(this); BIROperand dataOp = this.env.targetOperand; @@ -2723,7 +2726,7 @@ private void generateListConstructorExpr(BLangListConstructorExpr listConstructo BType referredType = Types.getImpliedType(listConstructorExprType); if (referredType.tag == TypeTags.ARRAY && ((BArrayType) referredType).state != BArrayState.OPEN) { - size = ((BArrayType) referredType).size; + size = ((BArrayType) referredType).getSize(); } else if (referredType.tag == TypeTags.TUPLE) { typedescOp = this.env.targetOperand; size = exprs.size(); @@ -2809,10 +2812,7 @@ private void generateMappingAccess(BLangIndexBasedAccess astIndexBasedAccessExpr if (astIndexBasedAccessExpr.getKind() == NodeKind.XML_ATTRIBUTE_ACCESS_EXPR) { insKind = InstructionKind.XML_ATTRIBUTE_STORE; keyRegIndex = getQNameOP(astIndexBasedAccessExpr.indexExpr, keyRegIndex); - } else if (astAccessExprExprType.tag == TypeTags.OBJECT || - (astAccessExprExprType.tag == TypeTags.UNION && - Types.getImpliedType(((BUnionType) astAccessExprExprType).getMemberTypes().iterator() - .next()).tag == TypeTags.OBJECT)) { + } else if (SemTypeHelper.isSubtypeSimple(astAccessExprExprType, PredefinedType.OBJECT)) { insKind = InstructionKind.OBJECT_STORE; } else { insKind = InstructionKind.MAP_STORE; @@ -2841,10 +2841,7 @@ private void generateMappingAccess(BLangIndexBasedAccess astIndexBasedAccessExpr keyRegIndex); this.varAssignment = false; return; - } else if (astAccessExprExprType.tag == TypeTags.OBJECT || - (astAccessExprExprType.tag == TypeTags.UNION && - Types.getImpliedType(((BUnionType) astAccessExprExprType).getMemberTypes().iterator() - .next()).tag == TypeTags.OBJECT)) { + } else if (SemTypeHelper.isSubtypeSimple(astAccessExprExprType, PredefinedType.OBJECT)) { insKind = InstructionKind.OBJECT_LOAD; } else { insKind = InstructionKind.MAP_LOAD; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCastGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCastGen.java index d9a5a619fb5d..d24945da2fc8 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCastGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCastGen.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.bir.codegen; +import io.ballerina.types.Env; import org.ballerinalang.compiler.BLangCompilerException; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; @@ -633,7 +634,7 @@ private void generateJCastToBAny(MethodVisitor mv, BIRVarToJVMIndexMap indexMap, mv.visitTypeInsn(INSTANCEOF, SIMPLE_VALUE); mv.visitJumpInsn(IFNE, afterHandle); } - if (isNillable(targetType)) { + if (targetType.isNullable()) { mv.visitInsn(DUP); mv.visitJumpInsn(IFNULL, afterHandle); } @@ -654,15 +655,6 @@ private void generateJCastToBAny(MethodVisitor mv, BIRVarToJVMIndexMap indexMap, } } - private static boolean isNillable(BType targetType) { - return switch (targetType.tag) { - case TypeTags.NIL, TypeTags.NEVER, TypeTags.JSON, TypeTags.ANY, TypeTags.ANYDATA, TypeTags.READONLY -> true; - case TypeTags.UNION, TypeTags.INTERSECTION, TypeTags.FINITE -> targetType.isNullable(); - case TypeTags.TYPEREFDESC -> isNillable(JvmCodeGenUtil.getImpliedType(targetType)); - default -> false; - }; - } - private void generateCheckCastJToBJSON(MethodVisitor mv, BIRVarToJVMIndexMap indexMap, JType sourceType) { if (sourceType.jTag == JTypeTags.JREF || sourceType.jTag == JTypeTags.JARRAY) { return; @@ -1130,7 +1122,7 @@ void generateCheckCastToByte(MethodVisitor mv, BType sourceType) { public void generateCheckCastToAnyData(MethodVisitor mv, BType type) { BType sourceType = JvmCodeGenUtil.getImpliedType(type); if (sourceType.tag == TypeTags.UNION || (types.isAssignable(sourceType, symbolTable.anyType) && - !Symbols.isFlagOn(sourceType.flags, Flags.READONLY))) { + !Symbols.isFlagOn(sourceType.getFlags(), Flags.READONLY))) { checkCast(mv, symbolTable.anydataType); } else { // if value types, then ad box instruction @@ -1407,4 +1399,9 @@ private void generateCastToAny(MethodVisitor mv, BType type) { private void generateXMLToAttributesMap(MethodVisitor mv) { mv.visitMethodInsn(INVOKEVIRTUAL, XML_VALUE, "getAttributesMap", GET_ATTRAIBUTE_MAP, false); } + + public Env typeEnv() { + assert types.typeEnv() != null; + return types.typeEnv(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java index 64d8c77249d9..379aef0495af 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmCodeGenUtil.java @@ -20,6 +20,7 @@ import io.ballerina.identifier.Utils; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import org.apache.commons.lang3.StringUtils; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.PackageID; @@ -230,8 +231,7 @@ public static String getFieldTypeSignature(BType bType) { case TypeTags.DECIMAL -> GET_BDECIMAL; case TypeTags.BOOLEAN -> "Z"; case TypeTags.NIL, TypeTags.NEVER, TypeTags.ANY, TypeTags.ANYDATA, TypeTags.UNION, TypeTags.JSON, - TypeTags.FINITE, TypeTags.READONLY -> - GET_OBJECT; + TypeTags.FINITE, TypeTags.READONLY -> GET_OBJECT; case TypeTags.MAP, TypeTags.RECORD -> GET_MAP_VALUE; case TypeTags.STREAM -> GET_STREAM_VALUE; case TypeTags.TABLE -> GET_TABLE_VALUE; @@ -334,18 +334,19 @@ private static String cleanupSourceFileName(String name) { return name.replace(".", FILE_NAME_PERIOD_SEPERATOR); } - public static String getMethodDesc(List paramTypes, BType retType) { - return INITIAL_METHOD_DESC + getMethodDescParams(paramTypes) + generateReturnType(retType); + public static String getMethodDesc(Env typeEnv, List paramTypes, BType retType) { + return INITIAL_METHOD_DESC + getMethodDescParams(paramTypes) + generateReturnType(retType, typeEnv); } - public static String getMethodDesc(List paramTypes, BType retType, BType attachedType) { + public static String getMethodDesc(Env typeEnv, List paramTypes, BType retType, BType attachedType) { return INITIAL_METHOD_DESC + getArgTypeSignature(attachedType) + getMethodDescParams(paramTypes) + - generateReturnType(retType); + generateReturnType(retType, typeEnv); } - public static String getMethodDesc(List paramTypes, BType retType, String attachedTypeClassName) { + public static String getMethodDesc(Env typeEnv, List paramTypes, BType retType, + String attachedTypeClassName) { return INITIAL_METHOD_DESC + "L" + attachedTypeClassName + ";" + getMethodDescParams(paramTypes) + - generateReturnType(retType); + generateReturnType(retType, typeEnv); } public static String getMethodDescParams(List paramTypes) { @@ -372,8 +373,7 @@ public static String getArgTypeSignature(BType bType) { case TypeTags.DECIMAL -> GET_BDECIMAL; case TypeTags.BOOLEAN -> "Z"; case TypeTags.NIL, TypeTags.NEVER, TypeTags.ANYDATA, TypeTags.UNION, TypeTags.JSON, TypeTags.FINITE, - TypeTags.ANY, TypeTags.READONLY -> - GET_OBJECT; + TypeTags.ANY, TypeTags.READONLY -> GET_OBJECT; case TypeTags.ARRAY, TypeTags.TUPLE -> GET_ARRAY_VALUE; case TypeTags.ERROR -> GET_ERROR_VALUE; case TypeTags.MAP, TypeTags.RECORD -> GET_MAP_VALUE; @@ -389,13 +389,13 @@ public static String getArgTypeSignature(BType bType) { }; } - public static String generateReturnType(BType bType) { + public static String generateReturnType(BType bType, Env typeEnv) { bType = JvmCodeGenUtil.getImpliedType(bType); if (bType == null) { return RETURN_JOBJECT; } - bType = JvmCodeGenUtil.UNIFIER.build(bType); + bType = JvmCodeGenUtil.UNIFIER.build(typeEnv, bType); if (bType == null || bType.tag == TypeTags.NIL || bType.tag == TypeTags.NEVER) { return RETURN_JOBJECT; } else if (TypeTags.isIntegerTypeTag(bType.tag)) { @@ -419,8 +419,7 @@ public static String generateReturnType(BType bType) { case TypeTags.FUTURE -> RETURN_FUTURE_VALUE; case TypeTags.TYPEDESC -> RETURN_TYPEDESC_VALUE; case TypeTags.ANY, TypeTags.ANYDATA, TypeTags.UNION, TypeTags.INTERSECTION, TypeTags.JSON, - TypeTags.FINITE, TypeTags.READONLY -> - RETURN_JOBJECT; + TypeTags.FINITE, TypeTags.READONLY -> RETURN_JOBJECT; case TypeTags.OBJECT -> RETURN_B_OBJECT; case TypeTags.INVOKABLE -> RETURN_FUNCTION_POINTER; case TypeTags.HANDLE -> RETURN_HANDLE_VALUE; @@ -629,12 +628,12 @@ public static void loadConstantValue(BType bType, Object constVal, MethodVisitor } } - private static String getStringConstantsClass(int varIndex, JvmConstantsGen jvmConstantsGen) { + static String getStringConstantsClass(int varIndex, JvmConstantsGen jvmConstantsGen) { int classIndex = varIndex / MAX_STRINGS_PER_METHOD; return jvmConstantsGen.getStringConstantsClass() + UNDERSCORE + classIndex; } - private static String removeDecimalDiscriminator(String value) { + static String removeDecimalDiscriminator(String value) { int length = value.length(); if (length < 2) { return value; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmDesugarPhase.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmDesugarPhase.java index 8f5a0f31cbc3..909ab80efe84 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmDesugarPhase.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmDesugarPhase.java @@ -19,6 +19,7 @@ package org.wso2.ballerinalang.compiler.bir.codegen; import io.ballerina.identifier.Utils; +import io.ballerina.types.Env; import org.ballerinalang.model.elements.PackageID; import org.wso2.ballerinalang.compiler.bir.codegen.methodgen.InitMethodGen; import org.wso2.ballerinalang.compiler.bir.model.BIRNode; @@ -61,9 +62,9 @@ public final class JvmDesugarPhase { private JvmDesugarPhase() { } - public static void addDefaultableBooleanVarsToSignature(BIRFunction func) { - func.type = new BInvokableType(func.type.paramTypes, func.type.restType, - func.type.retType, func.type.tsymbol); + public static void addDefaultableBooleanVarsToSignature(Env env, BIRFunction func) { + func.type = + new BInvokableType(env, func.type.paramTypes, func.type.restType, func.type.retType, func.type.tsymbol); BInvokableType type = func.type; func.type.paramTypes = updateParamTypesWithDefaultableBooleanVar(func.type.paramTypes, type.restType); @@ -94,7 +95,7 @@ private static List updateParamTypesWithDefaultableBooleanVar(List return paramTypes; } - static void rewriteRecordInits(List typeDefs) { + static void rewriteRecordInits(Env env, List typeDefs) { for (BIRTypeDefinition typeDef : typeDefs) { BType recordType = JvmCodeGenUtil.getImpliedType(typeDef.type); if (recordType.tag != TypeTags.RECORD) { @@ -102,12 +103,12 @@ static void rewriteRecordInits(List typeDefs) { } List attachFuncs = typeDef.attachedFuncs; for (BIRFunction func : attachFuncs) { - rewriteRecordInitFunction(func, (BRecordType) recordType); + rewriteRecordInitFunction(env, func, (BRecordType) recordType); } } } - private static void rewriteRecordInitFunction(BIRFunction func, BRecordType recordType) { + private static void rewriteRecordInitFunction(Env env, BIRFunction func, BRecordType recordType) { BIRVariableDcl receiver = func.receiver; @@ -129,7 +130,7 @@ private static void rewriteRecordInitFunction(BIRFunction func, BRecordType reco List updatedParamTypes = Lists.of(receiver.type); updatedParamTypes.addAll(func.type.paramTypes); - func.type = new BInvokableType(updatedParamTypes, func.type.restType, func.type.retType, null); + func.type = new BInvokableType(env, updatedParamTypes, func.type.restType, func.type.retType, null); List localVars = func.localVars; List updatedLocalVars = new ArrayList<>(); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmInstructionGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmInstructionGen.java index 7ba8024a01db..9f30abec25f5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmInstructionGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmInstructionGen.java @@ -1620,7 +1620,7 @@ void generateObjectNewIns(BIRNonTerminator.NewInstance objectNewIns, int strandI } private void reloadObjectCtorAnnots(BType type, int strandIndex) { - if ((type.flags & Flags.OBJECT_CTOR) == Flags.OBJECT_CTOR) { + if ((type.getFlags() & Flags.OBJECT_CTOR) == Flags.OBJECT_CTOR) { this.mv.visitTypeInsn(CHECKCAST, OBJECT_TYPE_IMPL); mv.visitMethodInsn(INVOKEVIRTUAL, OBJECT_TYPE_IMPL, "duplicate", OBJECT_TYPE_DUPLICATE, false); this.mv.visitInsn(DUP); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmObservabilityGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmObservabilityGen.java index 11231092bd9e..d655f0f47495 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmObservabilityGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmObservabilityGen.java @@ -19,6 +19,7 @@ import io.ballerina.identifier.Utils; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -53,6 +54,7 @@ import org.wso2.ballerinalang.compiler.bir.model.VarKind; import org.wso2.ballerinalang.compiler.bir.model.VarScope; import org.wso2.ballerinalang.compiler.diagnostic.BLangDiagnosticLocation; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.model.Scope; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BAttachedFunction; @@ -63,11 +65,9 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.SymTag; -import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.util.Name; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -181,7 +181,7 @@ public void instrumentPackage(BIRPackage pkg) { if ((typeDef.flags & Flags.CLASS) != Flags.CLASS && bType.tag == TypeTags.OBJECT) { continue; } - boolean isService = (bType.flags & Flags.SERVICE) == Flags.SERVICE; + boolean isService = (bType.getFlags() & Flags.SERVICE) == Flags.SERVICE; String serviceName = null; if (isService) { for (BIRNode.BIRAnnotationAttachment annotationAttachment : typeDef.annotAttachments) { @@ -360,7 +360,7 @@ private void rewriteAsyncInvocations(BIRFunction func, BIRTypeDefinition attache } Name lambdaName = new Name(LAMBDA_PREFIX + "observability" + lambdaIndex++ + "$" + asyncCallIns.name.getValue().replace(".", "_")); - BInvokableType bInvokableType = new BInvokableType(argTypes, null, + BInvokableType bInvokableType = new BInvokableType(symbolTable.typeEnv(), argTypes, null, returnType, null); BIRFunction desugaredFunc = new BIRFunction(asyncCallIns.pos, lambdaName, 0, bInvokableType, func.workerName, 0, VIRTUAL); @@ -986,20 +986,7 @@ private boolean isObservable(Call callIns) { * @return True if an error can be assigned and false otherwise */ private boolean isErrorAssignable(BIRVariableDcl variableDcl) { - boolean isErrorAssignable = false; - if (variableDcl.type instanceof BUnionType returnUnionType) { - boolean b = false; - for (BType type : returnUnionType.getMemberTypes()) { - if (type instanceof BErrorType) { - b = true; - break; - } - } - isErrorAssignable = b; - } else if (variableDcl.type instanceof BErrorType) { - isErrorAssignable = true; - } - return isErrorAssignable; + return SemTypeHelper.containsBasicType(variableDcl.type, PredefinedType.ERROR); } /** diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java index 65643e45ef72..f5066bc4f697 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmPackageGen.java @@ -19,6 +19,7 @@ package org.wso2.ballerinalang.compiler.bir.codegen; import io.ballerina.identifier.Utils; +import io.ballerina.types.Env; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.symbols.SymbolKind; @@ -61,7 +62,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.util.Name; import org.wso2.ballerinalang.compiler.util.Names; @@ -149,6 +149,7 @@ public class JvmPackageGen { private final BLangDiagnosticLog dlog; private final Types types; private final boolean isRemoteMgtEnabled; + private final Env typeEnv; JvmPackageGen(SymbolTable symbolTable, PackageCache packageCache, BLangDiagnosticLog dlog, Types types, boolean isRemoteMgtEnabled) { @@ -163,6 +164,7 @@ public class JvmPackageGen { initMethodGen = new InitMethodGen(symbolTable); configMethodGen = new ConfigMethodGen(); JvmInstructionGen.anyType = symbolTable.anyType; + this.typeEnv = symbolTable.typeEnv(); } private static String getBvmAlias(String orgName, String moduleName) { @@ -315,20 +317,21 @@ private static void setCurrentModuleField(ClassWriter cw, MethodVisitor mv, JvmC mv.visitFieldInsn(PUTSTATIC, moduleInitClass, CURRENT_MODULE_VAR_NAME, GET_MODULE); } - public static BIRFunctionWrapper getFunctionWrapper(BIRFunction currentFunc, PackageID packageID, + public static BIRFunctionWrapper getFunctionWrapper(Env typeEnv, BIRFunction currentFunc, PackageID packageID, String moduleClass) { BInvokableType functionTypeDesc = currentFunc.type; BIRVariableDcl receiver = currentFunc.receiver; BType retType = functionTypeDesc.retType; - if (isExternFunc(currentFunc) && Symbols.isFlagOn(retType.flags, Flags.PARAMETERIZED)) { - retType = unifier.build(retType); + if (isExternFunc(currentFunc) && Symbols.isFlagOn(retType.getFlags(), Flags.PARAMETERIZED)) { + retType = unifier.build(typeEnv, retType); } String jvmMethodDescription; if (receiver == null) { - jvmMethodDescription = JvmCodeGenUtil.getMethodDesc(functionTypeDesc.paramTypes, retType); + jvmMethodDescription = JvmCodeGenUtil.getMethodDesc(typeEnv, functionTypeDesc.paramTypes, retType); } else { - jvmMethodDescription = JvmCodeGenUtil.getMethodDesc(functionTypeDesc.paramTypes, retType, receiver.type); + jvmMethodDescription = JvmCodeGenUtil.getMethodDesc(typeEnv, functionTypeDesc.paramTypes, retType, + receiver.type); } return new BIRFunctionWrapper(packageID, currentFunc, moduleClass, jvmMethodDescription); } @@ -487,11 +490,12 @@ private void linkTypeDefinitions(BIRPackage module, boolean isEntry) { } private void linkModuleFunction(PackageID packageID, String initClass, String funcName) { - BInvokableType funcType = new BInvokableType(Collections.emptyList(), null, new BNilType(), null); + BInvokableType funcType = + new BInvokableType(typeEnv, Collections.emptyList(), null, symbolTable.nilType, null); BIRFunction moduleStopFunction = new BIRFunction(null, new Name(funcName), 0, funcType, new Name(""), 0, VIRTUAL); birFunctionMap.put(JvmCodeGenUtil.getPackageName(packageID) + funcName, - getFunctionWrapper(moduleStopFunction, packageID, initClass)); + getFunctionWrapper(typeEnv, moduleStopFunction, packageID, initClass)); } private void linkModuleFunctions(BIRPackage birPackage, String initClass, boolean isEntry, @@ -513,20 +517,20 @@ private void linkModuleFunctions(BIRPackage birPackage, String initClass, boolea PackageID packageID = birPackage.packageID; jvmClassMap.put(initClass, klass); String pkgName = JvmCodeGenUtil.getPackageName(packageID); - birFunctionMap.put(pkgName + functionName, getFunctionWrapper(initFunc, packageID, initClass)); + birFunctionMap.put(pkgName + functionName, getFunctionWrapper(typeEnv, initFunc, packageID, initClass)); count += 1; // Add start function BIRFunction startFunc = functions.get(1); functionName = Utils.encodeFunctionIdentifier(startFunc.name.value); - birFunctionMap.put(pkgName + functionName, getFunctionWrapper(startFunc, packageID, initClass)); + birFunctionMap.put(pkgName + functionName, getFunctionWrapper(typeEnv, startFunc, packageID, initClass)); klass.functions.add(1, startFunc); count += 1; // Add stop function BIRFunction stopFunc = functions.get(2); functionName = Utils.encodeFunctionIdentifier(stopFunc.name.value); - birFunctionMap.put(pkgName + functionName, getFunctionWrapper(stopFunc, packageID, initClass)); + birFunctionMap.put(pkgName + functionName, getFunctionWrapper(typeEnv, stopFunc, packageID, initClass)); klass.functions.add(2, stopFunc); count += 1; int genMethodsCount = 0; @@ -585,12 +589,13 @@ private BIRFunctionWrapper getBirFunctionWrapper(boolean isEntry, PackageID pack BIRFunction birFunc, String birModuleClassName) { BIRFunctionWrapper birFuncWrapperOrError; if (isExternFunc(birFunc) && isEntry) { - birFuncWrapperOrError = createExternalFunctionWrapper(true, birFunc, packageID, birModuleClassName); + birFuncWrapperOrError = createExternalFunctionWrapper(typeEnv, true, birFunc, packageID, + birModuleClassName); } else { if (isEntry && birFunc.receiver == null) { - addDefaultableBooleanVarsToSignature(birFunc); + addDefaultableBooleanVarsToSignature(typeEnv, birFunc); } - birFuncWrapperOrError = getFunctionWrapper(birFunc, packageID, birModuleClassName); + birFuncWrapperOrError = getFunctionWrapper(typeEnv, birFunc, packageID, birModuleClassName); } return birFuncWrapperOrError; } @@ -685,8 +690,8 @@ CompiledJarFile generate(BIRPackage module) { // use a ByteArrayOutputStream to store class byte values final JarEntries jarEntries = compiledJarFile.jarEntries; // desugar parameter initialization - injectDefaultParamInits(module, initMethodGen); - injectDefaultParamInitsToAttachedFuncs(module, initMethodGen); + injectDefaultParamInits(typeEnv, module, initMethodGen); + injectDefaultParamInitsToAttachedFuncs(typeEnv, module, initMethodGen); BIRFunction mainFunc = getMainFunction(module); BIRFunction testExecuteFunc = getTestExecuteFunction(module); @@ -717,7 +722,7 @@ CompiledJarFile generate(BIRPackage module) { removeSourceAnnotationTypeDefs(module.typeDefs); // desugar the record init function - rewriteRecordInits(module.typeDefs); + rewriteRecordInits(typeEnv, module.typeDefs); // generate object/record value classes JvmValueGen valueGen = new JvmValueGen(module, this, methodGen, typeHashVisitor, types); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java index 088e937ba560..42097b0a202c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTerminatorGen.java @@ -297,7 +297,7 @@ private void genGoToTerm(BIRTerminator.GOTO gotoIns, String funcName) { public void genReturnTerm(int returnVarRefIndex, BIRNode.BIRFunction func, int channelMapVarIndex, int sendWorkerChannelNamesVar, int receiveWorkerChannelNamesVar, int localVarOffset) { - BType bType = unifier.build(func.type.retType); + BType bType = unifier.build(symbolTable.typeEnv(), func.type.retType); generateReturnTermFromType(bType, func, returnVarRefIndex, channelMapVarIndex, sendWorkerChannelNamesVar, receiveWorkerChannelNamesVar, localVarOffset); } @@ -606,8 +606,8 @@ private void genStaticCall(BIRTerminator.Call callIns, PackageID packageID, int jvmClass = JvmCodeGenUtil.getModuleLevelClassName(packageID, JvmCodeGenUtil.cleanupPathSeparators(balFileName)); //TODO: add receiver: BType attachedType = type.r != null ? receiver.type : null; - BType retType = unifier.build(type.retType); - methodDesc = JvmCodeGenUtil.getMethodDesc(params, retType); + BType retType = unifier.build(symbolTable.typeEnv(), type.retType); + methodDesc = JvmCodeGenUtil.getMethodDesc(symbolTable.typeEnv(), params, retType); } this.mv.visitMethodInsn(INVOKESTATIC, jvmClass, encodedMethodName, methodDesc, false); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeGen.java index 491a1fa6b564..d5ed32a31e04 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeGen.java @@ -18,6 +18,16 @@ package org.wso2.ballerinalang.compiler.bir.codegen; import io.ballerina.identifier.Utils; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.symbols.SymbolKind; @@ -27,8 +37,7 @@ import org.objectweb.asm.MethodVisitor; import org.wso2.ballerinalang.compiler.bir.codegen.split.JvmConstantsGen; import org.wso2.ballerinalang.compiler.bir.model.BIRNode.BIRTypeDefinition; -import org.wso2.ballerinalang.compiler.semantics.analyzer.IsAnydataUniqueVisitor; -import org.wso2.ballerinalang.compiler.semantics.analyzer.IsPureTypeUniqueVisitor; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.analyzer.TypeHashVisitor; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableSymbol; @@ -52,16 +61,23 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.semantics.model.types.TypeFlags; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Set; +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.SemTypes.isSubtypeSimple; import static org.objectweb.asm.Opcodes.AASTORE; import static org.objectweb.asm.Opcodes.ACC_PUBLIC; import static org.objectweb.asm.Opcodes.ACC_STATIC; @@ -84,13 +100,17 @@ import static org.objectweb.asm.Opcodes.POP; import static org.objectweb.asm.Opcodes.RETURN; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmCodeGenUtil.getModuleLevelClassName; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmCodeGenUtil.getStringConstantsClass; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmCodeGenUtil.removeDecimalDiscriminator; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmCodeGenUtil.toNameString; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.ADD_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.BOOLEAN_VALUE; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.B_STRING_VAR_PREFIX; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CALL_FUNCTION; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CREATE_ERROR_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CREATE_OBJECT_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.CREATE_RECORD_VALUE; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.DECIMAL_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.DOUBLE_VALUE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.FINITE_TYPE_IMPL; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmConstants.FUNCTION_PARAMETER; @@ -162,6 +182,7 @@ import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_TABLE_TYPE_IMPL; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_TABLE_TYPE_WITH_FIELD_NAME_LIST; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_WITH_BOOLEAN; +import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INIT_WITH_STRING; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.INT_VALUE_OF_METHOD; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.LOAD_ANYDATA_TYPE; import static org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures.LOAD_ANY_TYPE; @@ -193,8 +214,6 @@ */ public class JvmTypeGen { - private final IsPureTypeUniqueVisitor isPureTypeUniqueVisitor; - private final IsAnydataUniqueVisitor isAnydataUniqueVisitor; private final JvmConstantsGen jvmConstantsGen; private final TypeHashVisitor typeHashVisitor; private final SymbolTable symbolTable; @@ -205,13 +224,12 @@ public class JvmTypeGen { private final String objectsClass; private final String errorsClass; private final String functionCallsClass; + private final Context semTypeCtx; public JvmTypeGen(JvmConstantsGen jvmConstantsGen, PackageID packageID, TypeHashVisitor typeHashVisitor, SymbolTable symbolTable) { this.jvmConstantsGen = jvmConstantsGen; this.packageID = packageID; - isPureTypeUniqueVisitor = new IsPureTypeUniqueVisitor(); - isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(); this.typeHashVisitor = typeHashVisitor; this.symbolTable = symbolTable; this.anonTypesClass = getModuleLevelClassName(packageID, MODULE_ANON_TYPES_CLASS_NAME); @@ -220,6 +238,7 @@ public JvmTypeGen(JvmConstantsGen jvmConstantsGen, PackageID packageID, TypeHash this.objectsClass = getModuleLevelClassName(packageID, MODULE_OBJECTS_CREATOR_CLASS_NAME); this.errorsClass = getModuleLevelClassName(packageID, MODULE_ERRORS_CREATOR_CLASS_NAME); this.functionCallsClass = getModuleLevelClassName(packageID, MODULE_FUNCTION_CALLS_CLASS_NAME); + this.semTypeCtx = Context.from(symbolTable.typeEnv()); } /** @@ -350,10 +369,10 @@ private void generateFunctionCallMethod(ClassWriter cw, String moduleClass) { } public int typeFlag(BType type) { - isAnydataUniqueVisitor.reset(); - isPureTypeUniqueVisitor.reset(); - return TypeFlags.asMask(type.isNullable(), isAnydataUniqueVisitor.visit(type), - isPureTypeUniqueVisitor.visit(type)); + boolean isAnydata = SemTypeHelper.isSubtype(semTypeCtx, type, Core.createAnydata(semTypeCtx)); + boolean isPureType = isAnydata || SemTypeHelper.isSubtype(semTypeCtx, type, + Core.union(Core.createAnydata(semTypeCtx), PredefinedType.ERROR)); + return TypeFlags.asMask(type.isNullable(), isAnydata, isPureType); } // ------------------------------------------------------- @@ -388,25 +407,27 @@ public void loadType(MethodVisitor mv, BType bType) { case TypeTags.BOOLEAN -> typeFieldName = "TYPE_BOOLEAN"; case TypeTags.BYTE -> typeFieldName = "TYPE_BYTE"; case TypeTags.ANY -> - typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? "TYPE_READONLY_ANY" : + typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_ANY" : + "TYPE_ANY"; case TypeTags.ANYDATA, TypeTags.REGEXP -> - typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? "TYPE_READONLY_ANYDATA" : + typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_ANYDATA" : "TYPE_ANYDATA"; case TypeTags.JSON -> - typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? "TYPE_READONLY_JSON" : + typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_JSON" : + "TYPE_JSON"; case TypeTags.XML -> { loadXmlType(mv, (BXMLType) bType); return; } case TypeTags.XML_ELEMENT -> - typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? "TYPE_READONLY_ELEMENT" : + typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_ELEMENT" : "TYPE_ELEMENT"; - case TypeTags.XML_PI -> typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? + case TypeTags.XML_PI -> typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_PROCESSING_INSTRUCTION" : "TYPE_PROCESSING_INSTRUCTION"; case TypeTags.XML_COMMENT -> - typeFieldName = Symbols.isFlagOn(bType.flags, Flags.READONLY) ? "TYPE_READONLY_COMMENT" : + typeFieldName = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? "TYPE_READONLY_COMMENT" : "TYPE_COMMENT"; case TypeTags.XML_TEXT -> typeFieldName = "TYPE_TEXT"; case TypeTags.TYPEDESC -> { @@ -507,8 +528,7 @@ private String loadTypeClass(BType bType) { return switch (bType.tag) { case TypeTags.NEVER -> LOAD_NEVER_TYPE; case TypeTags.INT, TypeTags.UNSIGNED8_INT, TypeTags.UNSIGNED16_INT, TypeTags.UNSIGNED32_INT, - TypeTags.SIGNED8_INT, TypeTags.SIGNED16_INT, TypeTags.SIGNED32_INT -> - LOAD_INTEGER_TYPE; + TypeTags.SIGNED8_INT, TypeTags.SIGNED16_INT, TypeTags.SIGNED32_INT -> LOAD_INTEGER_TYPE; case TypeTags.FLOAT -> LOAD_FLOAT_TYPE; case TypeTags.STRING, TypeTags.CHAR_STRING -> LOAD_STRING_TYPE; case TypeTags.DECIMAL -> LOAD_DECIMAL_TYPE; @@ -519,7 +539,7 @@ private String loadTypeClass(BType bType) { case TypeTags.JSON -> LOAD_JSON_TYPE; case TypeTags.XML, TypeTags.XML_TEXT -> LOAD_XML_TYPE; case TypeTags.XML_ELEMENT, TypeTags.XML_PI, TypeTags.XML_COMMENT -> - Symbols.isFlagOn(bType.flags, Flags.READONLY) ? LOAD_TYPE : LOAD_XML_TYPE; + Symbols.isFlagOn(bType.getFlags(), Flags.READONLY) ? LOAD_TYPE : LOAD_XML_TYPE; case TypeTags.OBJECT -> Symbols.isService(bType.tsymbol) ? LOAD_SERVICE_TYPE : LOAD_OBJECT_TYPE; case TypeTags.HANDLE -> LOAD_HANDLE_TYPE; case TypeTags.READONLY -> LOAD_READONLY_TYPE; @@ -570,7 +590,7 @@ private void loadMapType(MethodVisitor mv, BMapType bType) { } public void loadReadonlyFlag(MethodVisitor mv, BType bType) { - if (Symbols.isFlagOn(bType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(bType.getFlags(), Flags.READONLY)) { mv.visitInsn(ICONST_1); } else { mv.visitInsn(ICONST_0); @@ -670,7 +690,7 @@ private void loadErrorType(MethodVisitor mv, BErrorType errorType) { return; } - if (Symbols.isFlagOn(errorType.flags, Flags.ANONYMOUS)) { + if (Symbols.isFlagOn(errorType.getFlags(), Flags.ANONYMOUS)) { jvmConstantsGen.generateGetBErrorType(mv, jvmConstantsGen.getTypeConstantsVar(errorType, symbolTable)); } else { String typeOwner = JvmCodeGenUtil.getPackageName(pkgID) + MODULE_INIT_CLASS_NAME; @@ -815,7 +835,7 @@ private void loadUserDefinedType(MethodVisitor mv, BType bType) { boolean samePackage = JvmCodeGenUtil.isSameModule(this.packageID, pkgID); // if name contains $anon and doesn't belong to the same package, load type using getAnonType() method. - if (!samePackage && Symbols.isFlagOn(typeToLoad.flags, Flags.ANONYMOUS)) { + if (!samePackage && Symbols.isFlagOn(typeToLoad.getFlags(), Flags.ANONYMOUS)) { Integer hash = typeHashVisitor.visit(typeToLoad); String shape = typeToLoad.toString(); typeHashVisitor.reset(); @@ -876,8 +896,8 @@ public void loadInvokableType(MethodVisitor mv, BInvokableType bType) { mv.visitFieldInsn(GETSTATIC, jvmConstantsGen.getModuleConstantClass(), moduleName, GET_MODULE); } - if (Symbols.isFlagOn(bType.flags, Flags.ANY_FUNCTION)) { - mv.visitLdcInsn(bType.flags); + if (Symbols.isFlagOn(bType.getFlags(), Flags.ANY_FUNCTION)) { + mv.visitLdcInsn(bType.getFlags()); mv.visitMethodInsn(INVOKESPECIAL, FUNCTION_TYPE_IMPL, JVM_INIT_METHOD, INIT_FUNCTION_TYPE_IMPL, false); return; } @@ -894,7 +914,7 @@ public void loadInvokableType(MethodVisitor mv, BInvokableType bType) { // load return type loadType(mv, bType.retType); - mv.visitLdcInsn(bType.flags); + mv.visitLdcInsn(bType.getFlags()); mv.visitLdcInsn(bType.name.getValue()); // initialize the function type using the param types array and the return type mv.visitMethodInsn(INVOKESPECIAL, FUNCTION_TYPE_IMPL, JVM_INIT_METHOD, INIT_FUNCTION_TYPE_IMPL_WITH_PARAMS, @@ -1008,8 +1028,7 @@ public static String getTypeDesc(BType bType) { case TypeTags.FLOAT -> "D"; case TypeTags.BOOLEAN -> "Z"; case TypeTags.NIL, TypeTags.NEVER, TypeTags.ANY, TypeTags.ANYDATA, TypeTags.UNION, TypeTags.JSON, - TypeTags.FINITE, TypeTags.READONLY -> - GET_OBJECT; + TypeTags.FINITE, TypeTags.READONLY -> GET_OBJECT; case TypeTags.ARRAY, TypeTags.TUPLE -> GET_ARRAY_VALUE; case TypeTags.ERROR -> GET_ERROR_VALUE; case TypeTags.FUTURE -> GET_FUTURE_VALUE; @@ -1040,18 +1059,23 @@ private void loadFiniteType(MethodVisitor mv, BFiniteType finiteType) { mv.visitInsn(DUP); mv.visitMethodInsn(INVOKESPECIAL, LINKED_HASH_SET, JVM_INIT_METHOD, VOID_METHOD_DESC, false); - for (BLangExpression valueTypePair : finiteType.getValueSpace()) { - Object value = ((BLangLiteral) valueTypePair).value; - BType valueType = valueTypePair.getBType(); + for (SemNamedType semNamedType : finiteType.valueSpace) { mv.visitInsn(DUP); - - JvmCodeGenUtil.loadConstantValue(valueType, value, mv, jvmConstantsGen); - - if (TypeTags.isIntegerTypeTag(JvmCodeGenUtil.getImpliedType(valueType).tag)) { - mv.visitMethodInsn(INVOKESTATIC, LONG_VALUE, VALUE_OF_METHOD, LONG_VALUE_OF, - false); + SemType s = semNamedType.semType(); + if (PredefinedType.NIL.equals(s)) { + mv.visitInsn(ACONST_NULL); + } else if (isSubtypeSimple(s, PredefinedType.BOOLEAN)) { + loadConstBoolean(mv, (ComplexSemType) s); + } else if (isSubtypeSimple(s, PredefinedType.INT)) { + loadConstInteger(mv, (ComplexSemType) s); + } else if (isSubtypeSimple(s, PredefinedType.FLOAT)) { + loadConstFloat(mv, (ComplexSemType) s); + } else if (isSubtypeSimple(s, PredefinedType.DECIMAL)) { + loadConstDecimal(mv, (ComplexSemType) s); + } else if (isSubtypeSimple(s, PredefinedType.STRING)) { + loadConstString(mv, (ComplexSemType) s); } else { - loadValueType(mv, valueType); + throw new IllegalStateException("Unexpected value space type: " + s); } // Add the value to the set @@ -1066,16 +1090,42 @@ private void loadFiniteType(MethodVisitor mv, BFiniteType finiteType) { mv.visitMethodInsn(INVOKESPECIAL, FINITE_TYPE_IMPL, JVM_INIT_METHOD, INIT_FINITE_TYPE_IMPL, false); } - private void loadValueType(MethodVisitor mv, BType valueType) { - valueType = JvmCodeGenUtil.getImpliedType(valueType); - switch (valueType.tag) { - case TypeTags.BOOLEAN -> mv.visitMethodInsn(INVOKESTATIC, BOOLEAN_VALUE, VALUE_OF_METHOD, - BOOLEAN_VALUE_OF_METHOD, false); - case TypeTags.FLOAT -> mv.visitMethodInsn(INVOKESTATIC, DOUBLE_VALUE, VALUE_OF_METHOD, - DOUBLE_VALUE_OF_METHOD, false); - case TypeTags.BYTE -> mv.visitMethodInsn(INVOKESTATIC, INT_VALUE, VALUE_OF_METHOD, - INT_VALUE_OF_METHOD, false); + private void loadConstString(MethodVisitor mv, ComplexSemType s) { + String stringVal = StringSubtype.stringSubtypeSingleValue(getComplexSubtypeData(s, BT_STRING)).orElseThrow(); + int index = jvmConstantsGen.getBStringConstantVarIndex(stringVal); + String varName = B_STRING_VAR_PREFIX + index; + String stringConstantsClass = getStringConstantsClass(index, jvmConstantsGen); + mv.visitFieldInsn(GETSTATIC, stringConstantsClass, varName, GET_BSTRING); + } + + private static void loadConstDecimal(MethodVisitor mv, ComplexSemType s) { + BigDecimal bVal = DecimalSubtype.decimalSubtypeSingleValue(getComplexSubtypeData(s, BT_DECIMAL)).orElseThrow(); + mv.visitTypeInsn(NEW, DECIMAL_VALUE); + mv.visitInsn(DUP); + mv.visitLdcInsn(removeDecimalDiscriminator(String.valueOf(bVal))); + mv.visitMethodInsn(INVOKESPECIAL, DECIMAL_VALUE, JVM_INIT_METHOD, INIT_WITH_STRING, false); + } + + private static void loadConstFloat(MethodVisitor mv, ComplexSemType s) { + double doubleVal = FloatSubtype.floatSubtypeSingleValue(getComplexSubtypeData(s, BT_FLOAT)).orElseThrow(); + mv.visitLdcInsn(doubleVal); + mv.visitMethodInsn(INVOKESTATIC, DOUBLE_VALUE, VALUE_OF_METHOD, DOUBLE_VALUE_OF_METHOD, false); + } + + private static void loadConstInteger(MethodVisitor mv, ComplexSemType s) { + long longVal = IntSubtype.intSubtypeSingleValue(getComplexSubtypeData(s, BT_INT)).orElseThrow(); + if (0 <= longVal && longVal <= 255) { + mv.visitLdcInsn((int) longVal); + mv.visitMethodInsn(INVOKESTATIC, INT_VALUE, VALUE_OF_METHOD, INT_VALUE_OF_METHOD, false); + } else { + mv.visitLdcInsn(longVal); + mv.visitMethodInsn(INVOKESTATIC, LONG_VALUE, VALUE_OF_METHOD, LONG_VALUE_OF, false); } } + private static void loadConstBoolean(MethodVisitor mv, ComplexSemType s) { + boolean boolVal = BooleanSubtype.booleanSubtypeSingleValue(getComplexSubtypeData(s, BT_BOOLEAN)).orElseThrow(); + mv.visitLdcInsn(boolVal); + mv.visitMethodInsn(INVOKESTATIC, BOOLEAN_VALUE, VALUE_OF_METHOD, BOOLEAN_VALUE_OF_METHOD, false); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeTestGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeTestGen.java index cd3a61752802..399b7cfea49b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeTestGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmTypeTestGen.java @@ -18,13 +18,15 @@ package org.wso2.ballerinalang.compiler.bir.codegen; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.objectweb.asm.Label; import org.objectweb.asm.MethodVisitor; import org.wso2.ballerinalang.compiler.bir.model.BIRNonTerminator; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.util.TypeTags; import static org.objectweb.asm.Opcodes.GOTO; import static org.objectweb.asm.Opcodes.ICONST_0; @@ -86,106 +88,92 @@ void generateTypeTestIns(BIRNonTerminator.TypeTest typeTestIns) { } /** - * Checks if the type tested for is nil. That is the target type is nil. Example instructions include 'a is ()' - * where 'a' is a variable of type say any or a union with nil. + * Checks if we have x is (). + *
+ * In that case we can simplify is-check to a x instanceof null check. * * @param sourceType the declared variable type * @param targetType the RHS type in the type check instruction. Type to be tested for * @return whether instruction could be optimized using 'instanceof` check */ private boolean canOptimizeNilCheck(BType sourceType, BType targetType) { - return JvmCodeGenUtil.getImpliedType(targetType).tag == TypeTags.NIL && - types.isAssignable(targetType, sourceType); + return PredefinedType.NIL.equals(targetType.semType()) && + SemTypes.containsBasicType(sourceType.semType(), PredefinedType.NIL); } /** - * This checks for any variable declaration containing a nil in a union of two types. Examples include string? or - * error? or int?. + * Checks if we have x is T in which x's static type is T'=T|(). + *
+ * In that case we can simplify is-check to a !(x instanceof null) check. * * @param sourceType the declared variable type * @param targetType the RHS type in the type check instruction. Type to be tested for * @return whether instruction could be optimized using 'instanceof` check for null */ private boolean canOptimizeNilUnionCheck(BType sourceType, BType targetType) { - sourceType = JvmCodeGenUtil.getImpliedType(sourceType); - if (isInValidUnionType(sourceType)) { + SemType sourceTy = sourceType.semType(); + if (!SemTypes.containsBasicType(sourceTy, PredefinedType.NIL)) { return false; } - boolean foundNil = false; - BType otherType = null; - for (BType bType : ((BUnionType) sourceType).getMemberTypes()) { - if (JvmCodeGenUtil.getImpliedType(bType).tag == TypeTags.NIL) { - foundNil = true; - } else { - otherType = bType; - } + + SemType tyButNil = Core.diff(sourceTy, PredefinedType.NIL); + if (Core.isNever(tyButNil)) { + return false; } - return foundNil && targetType.equals(otherType); + return SemTypes.isSameType(types.typeCtx(), tyButNil, targetType.semType()); } /** - * Checks if the type tested for is error. That is the target type is error. Example instructions include 'a is - * error' where 'a' is a variable of type say any or a union with nil. + * Checks if we have x is E where E is a subtype of error and error part + * of x is a subtype of E. + *
+ * In that case we can simplify is-check to a x instanceof BError check. * * @param sourceType the declared variable type * @param targetType the RHS type in the type check instruction. Type to be tested for * @return whether instruction could be optimized using 'instanceof` check for BError */ private boolean canOptimizeErrorCheck(BType sourceType, BType targetType) { - sourceType = JvmCodeGenUtil.getImpliedType(sourceType); - targetType = JvmCodeGenUtil.getImpliedType(targetType); - if (targetType.tag != TypeTags.ERROR || sourceType.tag != TypeTags.UNION) { + SemType targetTy = targetType.semType(); + if (!Core.isSubtypeSimple(targetTy, PredefinedType.ERROR)) { return false; } - BType errorType = null; - int foundError = 0; - for (BType bType : ((BUnionType) sourceType).getMemberTypes()) { - if (bType.tag == TypeTags.ERROR) { - foundError++; - errorType = bType; - } + + SemType errIntersect = SemTypes.intersect(sourceType.semType(), PredefinedType.ERROR); + if (Core.isNever(errIntersect)) { + return false; } - return (foundError == 1 && types.isAssignable(errorType, targetType)) || (foundError > 0 && "error".equals( - targetType.tsymbol.name.value)); + return SemTypes.isSubtype(types.typeCtx(), errIntersect, targetTy); } /** - * This checks for any variable declaration containing a error in a union of two types. Examples include - * string|error or error|error or int|error. + * Checks if we have x is T in which x's static type is T'=T|E where + * E is a non-empty error type. + *
+ * In that case we can simplify is-check to a !(x instanceof BError) check. * * @param sourceType the declared variable type * @param targetType the RHS type in the type check instruction. Type to be tested for * @return whether instruction could be optimized using 'instanceof` check for BError */ private boolean canOptimizeErrorUnionCheck(BType sourceType, BType targetType) { - sourceType = JvmCodeGenUtil.getImpliedType(sourceType); - if (isInValidUnionType(sourceType)) { + SemType sourceTy = sourceType.semType(); + if (!SemTypes.containsBasicType(sourceTy, PredefinedType.ERROR)) { return false; } - BType otherType = null; - int foundError = 0; - for (BType bType : ((BUnionType) sourceType).getMemberTypes()) { - if (JvmCodeGenUtil.getImpliedType(bType).tag == TypeTags.ERROR) { - foundError++; - } else { - otherType = bType; - } - } - return foundError == 1 && targetType.equals(otherType); - } - private boolean isInValidUnionType(BType rhsType) { - if (rhsType.tag != TypeTags.UNION) { - return true; + SemType tyButError = Core.diff(sourceTy, PredefinedType.ERROR); + if (Core.isNever(tyButError)) { + return false; } - return ((BUnionType) rhsType).getMemberTypes().size() != 2; + return SemTypes.isSameType(types.typeCtx(), tyButError, targetType.semType()); } private void handleNilUnionType(BIRNonTerminator.TypeTest typeTestIns) { jvmInstructionGen.loadVar(typeTestIns.rhsOp.variableDcl); jvmCastGen.addBoxInsn(this.mv, typeTestIns.rhsOp.variableDcl.type); Label ifLabel = new Label(); - if (JvmCodeGenUtil.getImpliedType(typeTestIns.type).tag == TypeTags.NIL) { + if (PredefinedType.NIL.equals(typeTestIns.type.semType())) { mv.visitJumpInsn(IFNONNULL, ifLabel); } else { mv.visitJumpInsn(IFNULL, ifLabel); @@ -206,7 +194,7 @@ private void loadBoolean(Label ifLabel) { private void handleErrorUnionType(BIRNonTerminator.TypeTest typeTestIns) { jvmInstructionGen.loadVar(typeTestIns.rhsOp.variableDcl); mv.visitTypeInsn(INSTANCEOF, BERROR); - if (JvmCodeGenUtil.getImpliedType(typeTestIns.type).tag != TypeTags.ERROR) { + if (!Core.isSubtypeSimple(typeTestIns.type.semType(), PredefinedType.ERROR)) { generateNegateBoolean(); } jvmInstructionGen.storeToVar(typeTestIns.lhsOp.variableDcl); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmValueGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmValueGen.java index 7d97b9ab36c9..e8266b89bc75 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmValueGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmValueGen.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.bir.codegen; +import io.ballerina.types.Env; import org.ballerinalang.model.elements.PackageID; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.FieldVisitor; @@ -137,28 +138,29 @@ public class JvmValueGen { this.types = types; } - static void injectDefaultParamInitsToAttachedFuncs(BIRNode.BIRPackage module, InitMethodGen initMethodGen) { + static void injectDefaultParamInitsToAttachedFuncs(Env env, BIRNode.BIRPackage module, + InitMethodGen initMethodGen) { List typeDefs = module.typeDefs; for (BIRNode.BIRTypeDefinition optionalTypeDef : typeDefs) { BType bType = JvmCodeGenUtil.getImpliedType(optionalTypeDef.type); if ((bType.tag == TypeTags.OBJECT && Symbols.isFlagOn( bType.tsymbol.flags, Flags.CLASS)) || bType.tag == TypeTags.RECORD) { - desugarObjectMethods(optionalTypeDef.attachedFuncs, initMethodGen); + desugarObjectMethods(env, optionalTypeDef.attachedFuncs, initMethodGen); } } } - private static void desugarObjectMethods(List attachedFuncs, InitMethodGen initMethodGen) { + private static void desugarObjectMethods(Env env, List attachedFuncs, InitMethodGen initMethodGen) { for (BIRNode.BIRFunction birFunc : attachedFuncs) { if (JvmCodeGenUtil.isExternFunc(birFunc)) { if (birFunc instanceof JMethodBIRFunction jMethodBIRFunction) { - desugarInteropFuncs(jMethodBIRFunction, initMethodGen); + desugarInteropFuncs(env, jMethodBIRFunction, initMethodGen); initMethodGen.resetIds(); } else if (!(birFunc instanceof JFieldBIRFunction)) { initMethodGen.resetIds(); } } else { - addDefaultableBooleanVarsToSignature(birFunc); + addDefaultableBooleanVarsToSignature(env, birFunc); initMethodGen.resetIds(); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/ExternalMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/ExternalMethodGen.java index 5d25387eb63e..65286884f9f3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/ExternalMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/ExternalMethodGen.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.bir.codegen.interop; +import io.ballerina.types.Env; import org.ballerinalang.model.elements.PackageID; import org.objectweb.asm.ClassWriter; import org.wso2.ballerinalang.compiler.bir.codegen.JvmCastGen; @@ -63,7 +64,7 @@ public static void genJMethodForBExternalFunc(BIRFunction birFunc, ClassWriter c } } - public static void injectDefaultParamInits(BIRPackage module, InitMethodGen initMethodGen) { + public static void injectDefaultParamInits(Env typeEnv, BIRPackage module, InitMethodGen initMethodGen) { // filter out functions. List functions = module.functions; if (!functions.isEmpty()) { @@ -74,7 +75,7 @@ public static void injectDefaultParamInits(BIRPackage module, InitMethodGen init BIRFunction birFunc = functions.get(count); count = count + 1; if (birFunc instanceof JMethodBIRFunction jMethodBIRFunction) { - desugarInteropFuncs(jMethodBIRFunction, initMethodGen); + desugarInteropFuncs(typeEnv, jMethodBIRFunction, initMethodGen); initMethodGen.resetIds(); } else if (!(birFunc instanceof JFieldBIRFunction)) { initMethodGen.resetIds(); @@ -83,12 +84,12 @@ public static void injectDefaultParamInits(BIRPackage module, InitMethodGen init } } - public static BIRFunctionWrapper createExternalFunctionWrapper(boolean isEntry, BIRFunction birFunc, + public static BIRFunctionWrapper createExternalFunctionWrapper(Env env, boolean isEntry, BIRFunction birFunc, PackageID packageID, String birModuleClassName) { if (isEntry) { - addDefaultableBooleanVarsToSignature(birFunc); + addDefaultableBooleanVarsToSignature(env, birFunc); } - return getFunctionWrapper(birFunc, packageID, birModuleClassName); + return getFunctionWrapper(env, birFunc, packageID, birModuleClassName); } private ExternalMethodGen() { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java index 0b2d373cf1e9..37c764a58496 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropMethodGen.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.bir.codegen.interop; +import io.ballerina.types.Env; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.PackageID; import org.objectweb.asm.ClassWriter; @@ -141,11 +142,11 @@ static void genJFieldForInteropField(JFieldBIRFunction birFunc, ClassWriter clas // Generate method desc BType retType = birFunc.type.retType; - if (Symbols.isFlagOn(retType.flags, Flags.PARAMETERIZED)) { - retType = JvmCodeGenUtil.UNIFIER.build(birFunc.type.retType); + if (Symbols.isFlagOn(retType.getFlags(), Flags.PARAMETERIZED)) { + retType = JvmCodeGenUtil.UNIFIER.build(types.typeEnv(), birFunc.type.retType); } - String desc = JvmCodeGenUtil.getMethodDesc(birFunc.type.paramTypes, retType); + String desc = JvmCodeGenUtil.getMethodDesc(types.typeEnv(), birFunc.type.paramTypes, retType); int access = birFunc.receiver != null ? ACC_PUBLIC : ACC_PUBLIC + ACC_STATIC; MethodVisitor mv = classWriter.visitMethod(access, birFunc.name.value, desc, null, null); JvmInstructionGen instGen = new JvmInstructionGen(mv, indexMap, birModule, jvmPackageGen, jvmTypeGen, @@ -257,11 +258,11 @@ static void genJFieldForInteropField(JFieldBIRFunction birFunc, ClassWriter clas mv.visitEnd(); } - public static void desugarInteropFuncs(JMethodBIRFunction birFunc, InitMethodGen initMethodGen) { + public static void desugarInteropFuncs(Env typeEnv, JMethodBIRFunction birFunc, InitMethodGen initMethodGen) { // resetting the variable generation index BType retType = birFunc.type.retType; - if (Symbols.isFlagOn(retType.flags, Flags.PARAMETERIZED)) { - retType = JvmCodeGenUtil.UNIFIER.build(birFunc.type.retType); + if (Symbols.isFlagOn(retType.getFlags(), Flags.PARAMETERIZED)) { + retType = JvmCodeGenUtil.UNIFIER.build(typeEnv, birFunc.type.retType); } JMethod jMethod = birFunc.jMethod; Class[] jMethodParamTypes = jMethod.getParamTypes(); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropValidator.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropValidator.java index 59be56de92d3..f20f50ef2e2a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropValidator.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/InteropValidator.java @@ -204,7 +204,8 @@ private ClassLoader makeClassLoader(Set moduleDependencies) { JMethod validateAndGetJMethod(InteropValidationRequest.MethodValidationRequest methodValidationRequest, ClassLoader classLoader) { // Populate JMethodRequest from the BValue - JMethodRequest jMethodRequest = JMethodRequest.build(methodValidationRequest, classLoader); + JMethodRequest jMethodRequest = JMethodRequest.build(symbolTable.typeEnv(), methodValidationRequest, + classLoader); // Find the most specific Java method or constructor for the given request JMethodResolver methodResolver = new JMethodResolver(classLoader, symbolTable); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodRequest.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodRequest.java index dfa5278a93e8..c52cd1f4ab49 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodRequest.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodRequest.java @@ -17,14 +17,15 @@ */ package org.wso2.ballerinalang.compiler.bir.codegen.interop; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.symbols.SymbolKind; import org.wso2.ballerinalang.compiler.bir.codegen.model.JMethodKind; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.compiler.util.Unifier; import java.util.ArrayList; @@ -59,7 +60,7 @@ private JMethodRequest() { } - static JMethodRequest build(InteropValidationRequest.MethodValidationRequest methodValidationRequest, + static JMethodRequest build(Env typeEnv, InteropValidationRequest.MethodValidationRequest methodValidationRequest, ClassLoader classLoader) { JMethodRequest jMethodReq = new JMethodRequest(); @@ -94,18 +95,9 @@ static JMethodRequest build(InteropValidationRequest.MethodValidationRequest met jMethodReq.bParamTypes = paramTypes.toArray(new BType[0]); jMethodReq.pathParamSymbols = pathParams; - BType returnType = unifier.build(bFuncType.retType); + BType returnType = unifier.build(typeEnv, bFuncType.retType); jMethodReq.bReturnType = returnType; - if (returnType.tag == TypeTags.UNION) { - for (BType bType : ((BUnionType) returnType).getMemberTypes()) { - if (bType.tag == TypeTags.ERROR) { - jMethodReq.returnsBErrorType = true; - break; - } - } - } else { - jMethodReq.returnsBErrorType = returnType.tag == TypeTags.ERROR; - } + jMethodReq.returnsBErrorType = SemTypes.containsBasicType(returnType.semType(), PredefinedType.ERROR); jMethodReq.restParamExist = methodValidationRequest.restParamExist; return jMethodReq; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java index 3289cfa31446..8f695617002f 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodResolver.java @@ -37,6 +37,7 @@ import org.wso2.ballerinalang.compiler.bir.codegen.exceptions.JInteropException; import org.wso2.ballerinalang.compiler.bir.codegen.model.JMethod; import org.wso2.ballerinalang.compiler.bir.codegen.model.JMethodKind; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; @@ -45,7 +46,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeReferenceType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; @@ -253,7 +253,7 @@ private boolean hasEquivalentFunctionParamCount(JMethodRequest jMethodRequest, J // https://github.com/ballerina-platform/ballerina-lang/issues/42456. if (jMethodRequest.receiverType == null || functionParamCount < 1 || count < reducedParamCount || count > reducedParamCount + 2 - || Symbols.isFlagOn(jMethodRequest.bParamTypes[0].flags, Flags.SERVICE)) { + || Symbols.isFlagOn(jMethodRequest.bParamTypes[0].getFlags(), Flags.SERVICE)) { return false; } if (!isParamAssignableToBArray(paramTypes[count - 1]) @@ -387,18 +387,6 @@ private void validateExceptionTypes(JMethodRequest jMethodRequest, JMethod jMeth "Incompatible ballerina return type for Java method '" + jMethodRequest.methodName + "' which " + "throws checked exception found in class '" + jMethodRequest.declaringClass.getName() + "': expected '" + expectedRetTypeName + "', found '" + returnType + "'"); - } else if (jMethodRequest.returnsBErrorType && !throwsCheckedException && !returnsErrorValue) { - String errorMsgPart; - if (returnType instanceof BUnionType bUnionReturnType) { - BType modifiedRetType = BUnionType.create(null, getNonErrorMembers(bUnionReturnType)); - errorMsgPart = "expected '" + modifiedRetType + "', found '" + returnType + "'"; - } else { - errorMsgPart = "no return type expected but found '" + returnType + "'"; - } - throw new JInteropException(DiagnosticErrorCode.METHOD_SIGNATURE_DOES_NOT_MATCH, - "Incompatible ballerina return type for Java method '" + jMethodRequest.methodName + "' which " + - "throws 'java.lang.RuntimeException' found in class '" + - jMethodRequest.declaringClass.getName() + "': " + errorMsgPart); } } @@ -407,7 +395,8 @@ private String getExpectedReturnType(BType retType) { ((BTypeReferenceType) retType).referredType.tag == TypeTags.ERROR)) { return "error"; } else if (retType instanceof BUnionType bUnionReturnType) { - BType modifiedRetType = BUnionType.create(null, getNonErrorMembers(bUnionReturnType)); + BType modifiedRetType = + BUnionType.create(symbolTable.typeEnv(), null, getNonErrorMembers(bUnionReturnType)); return modifiedRetType + "|error"; } else { return retType + "|error"; @@ -510,7 +499,7 @@ private void bundlePathParams(JMethodRequest jMethodRequest, JMethod jMethod) { for (BVarSymbol param : pathParamSymbols) { paramTypes.remove(param.type); } - paramTypes.add(initialPathParamIndex, new BArrayType(symbolTable.anydataType)); + paramTypes.add(initialPathParamIndex, new BArrayType(symbolTable.typeEnv(), symbolTable.anydataType)); jMethodRequest.bParamTypes = paramTypes.toArray(new BType[0]); jMethodRequest.bFuncParamCount = jMethodRequest.bFuncParamCount - pathParamSymbols.size() + 1; jMethodRequest.pathParamCount = 1; @@ -522,7 +511,7 @@ private void bundleFunctionParams(JMethodRequest jMethodRequest, JMethod jMethod if (jMethodRequest.bFuncParamCount > jMethodRequest.pathParamCount) { paramTypes.subList(jMethodRequest.pathParamCount, jMethodRequest.bFuncParamCount).clear(); } - paramTypes.add(new BArrayType(symbolTable.anyType)); + paramTypes.add(new BArrayType(symbolTable.typeEnv(), symbolTable.anyType)); jMethodRequest.bParamTypes = paramTypes.toArray(new BType[0]); jMethodRequest.bFuncParamCount = jMethodRequest.pathParamCount + 1; jMethod.hasBundledFunctionParams = true; @@ -530,8 +519,8 @@ private void bundleFunctionParams(JMethodRequest jMethodRequest, JMethod jMethod private void bundleBothPathAndFunctionParameter(JMethodRequest jMethodRequest, JMethod jMethod) { List paramTypes = new ArrayList<>(); - paramTypes.add(new BArrayType(symbolTable.anydataType)); - paramTypes.add(new BArrayType(symbolTable.anyType)); + paramTypes.add(new BArrayType(symbolTable.typeEnv(), symbolTable.anydataType)); + paramTypes.add(new BArrayType(symbolTable.typeEnv(), symbolTable.anyType)); jMethodRequest.bParamTypes = paramTypes.toArray(new BType[0]); jMethodRequest.bFuncParamCount = 2; jMethodRequest.pathParamCount = 1; @@ -640,9 +629,8 @@ private boolean isInvalidParamBType(Class jType, BType bType, boolean isLastP if (jTypeName.equals(J_OBJECT_TNAME)) { return false; } - Set valueSpace = ((BFiniteType) bType).getValueSpace(); - for (BLangExpression value : valueSpace) { - if (isInvalidParamBType(jType, value.getBType(), isLastParam, restParamExist)) { + for (BType t : SemTypeHelper.broadTypes((BFiniteType) bType, symbolTable)) { + if (isInvalidParamBType(jType, t, isLastParam, restParamExist)) { return true; } } @@ -795,9 +783,8 @@ private boolean isValidReturnBType(Class jType, BType bType, JMethodRequest j if (jTypeName.equals(J_OBJECT_TNAME)) { return true; } - Set valueSpace = ((BFiniteType) bType).getValueSpace(); - for (BLangExpression value : valueSpace) { - if (isValidReturnBType(jType, value.getBType(), jMethodRequest, visitedSet)) { + for (BType t : SemTypeHelper.broadTypes((BFiniteType) bType, symbolTable)) { + if (isValidReturnBType(jType, t, jMethodRequest, visitedSet)) { return true; } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java index 95692b46bd37..2dd4a90d648c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/InitMethodGen.java @@ -131,7 +131,8 @@ public class InitMethodGen { public InitMethodGen(SymbolTable symbolTable) { this.symbolTable = symbolTable; - this.errorOrNilType = BUnionType.create(null, symbolTable.errorType, symbolTable.nilType); + this.errorOrNilType = + BUnionType.create(symbolTable.typeEnv(), null, symbolTable.errorType, symbolTable.nilType); } /** @@ -185,7 +186,7 @@ public void generateLambdaForModuleExecuteFunction(ClassWriter cw, String initCl jvmCastGen.addUnboxInsn(mv, paramType); paramIndex += 1; } - methodDesc = JvmCodeGenUtil.getMethodDesc(paramTypes, returnType); + methodDesc = JvmCodeGenUtil.getMethodDesc(symbolTable.typeEnv(), paramTypes, returnType); } mv.visitMethodInsn(INVOKESTATIC, initClass, MODULE_EXECUTE_METHOD, methodDesc, false); @@ -331,21 +332,21 @@ public void enrichPkgWithInitializers(Map birFunctio javaClass.functions.add(initFunc); pkg.functions.add(initFunc); birFunctionMap.put(JvmCodeGenUtil.getPackageName(pkg.packageID) + MODULE_INIT_METHOD, - JvmPackageGen.getFunctionWrapper(initFunc, pkg.packageID, typeOwnerClass)); + JvmPackageGen.getFunctionWrapper(symbolTable.typeEnv(), initFunc, pkg.packageID, typeOwnerClass)); BIRNode.BIRFunction startFunc = generateDefaultFunction(moduleImports, pkg, MODULE_START_METHOD, MethodGenUtils.START_FUNCTION_SUFFIX); javaClass.functions.add(startFunc); pkg.functions.add(startFunc); birFunctionMap.put(JvmCodeGenUtil.getPackageName(pkg.packageID) + MODULE_START_METHOD, - JvmPackageGen.getFunctionWrapper(startFunc, pkg.packageID, typeOwnerClass)); + JvmPackageGen.getFunctionWrapper(symbolTable.typeEnv(), startFunc, pkg.packageID, typeOwnerClass)); BIRNode.BIRFunction execFunc = generateExecuteFunction(pkg, mainFunc, testExecuteFunc ); javaClass.functions.add(execFunc); pkg.functions.add(execFunc); birFunctionMap.put(JvmCodeGenUtil.getPackageName(pkg.packageID) + MODULE_EXECUTE_METHOD, - JvmPackageGen.getFunctionWrapper(execFunc, pkg.packageID, typeOwnerClass)); + JvmPackageGen.getFunctionWrapper(symbolTable.typeEnv(), execFunc, pkg.packageID, typeOwnerClass)); } private BIRNode.BIRFunction generateExecuteFunction(BIRNode.BIRPackage pkg, @@ -355,7 +356,8 @@ private BIRNode.BIRFunction generateExecuteFunction(BIRNode.BIRPackage pkg, new Name("%ret"), VarScope.FUNCTION, VarKind.RETURN, null); BIROperand retVarRef = new BIROperand(retVar); List functionArgs = new ArrayList<>(); - BInvokableType funcType = new BInvokableType(Collections.emptyList(), null, errorOrNilType, null); + BInvokableType funcType = + new BInvokableType(symbolTable.typeEnv(), Collections.emptyList(), null, errorOrNilType, null); BIRNode.BIRFunction modExecFunc = new BIRNode.BIRFunction(null, new Name(MODULE_EXECUTE_METHOD), 0, funcType, null, 0, VIRTUAL); List paramTypes = new ArrayList<>(); @@ -511,7 +513,8 @@ private BIRNode.BIRFunction generateDefaultFunction(Set imprtMods, BI VarScope.FUNCTION, VarKind.RETURN, null); BIROperand retVarRef = new BIROperand(retVar); - BInvokableType funcType = new BInvokableType(Collections.emptyList(), null, errorOrNilType, null); + BInvokableType funcType = + new BInvokableType(symbolTable.typeEnv(), Collections.emptyList(), null, errorOrNilType, null); BIRNode.BIRFunction modInitFunc = new BIRNode.BIRFunction(symbolTable.builtinPos, new Name(funcName), 0, funcType, null, 0, VIRTUAL); modInitFunc.localVars.add(retVar); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/LambdaGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/LambdaGen.java index c20f64b0449d..bf4aa7156b97 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/LambdaGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/LambdaGen.java @@ -457,7 +457,7 @@ private String getLambdaMethodDesc(List paramTypes, BType retType, int cl StringBuilder desc = new StringBuilder(INITIAL_METHOD_DESC); appendClosureMaps(closureMapsCount, desc); appendParamTypes(paramTypes, desc); - desc.append(JvmCodeGenUtil.generateReturnType(retType)); + desc.append(JvmCodeGenUtil.generateReturnType(retType, jvmCastGen.typeEnv())); return desc.toString(); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java index fd6d717c40fc..d4ab30941346 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/MethodGen.java @@ -19,6 +19,7 @@ package org.wso2.ballerinalang.compiler.bir.codegen.methodgen; import io.ballerina.identifier.Utils; +import io.ballerina.types.Env; import org.ballerinalang.compiler.BLangCompilerException; import org.ballerinalang.model.elements.PackageID; import org.objectweb.asm.ClassWriter; @@ -147,11 +148,13 @@ public class MethodGen { private final JvmPackageGen jvmPackageGen; private final SymbolTable symbolTable; private final Types types; + private final Env typeEnv; public MethodGen(JvmPackageGen jvmPackageGen, Types types) { this.jvmPackageGen = jvmPackageGen; this.symbolTable = jvmPackageGen.symbolTable; this.types = types; + this.typeEnv = types.typeEnv(); } public void generateMethod(BIRFunction birFunc, ClassWriter cw, BIRPackage birModule, BType attachedType, @@ -177,7 +180,7 @@ public void genJMethodWithBObjectMethodCall(BIRFunction func, ClassWriter cw, BI indexMap.addIfNotExists(STRAND, symbolTable.stringType); String funcName = func.name.value; BType retType = getReturnType(func); - String desc = JvmCodeGenUtil.getMethodDesc(func.type.paramTypes, retType); + String desc = JvmCodeGenUtil.getMethodDesc(typeEnv, func.type.paramTypes, retType); MethodVisitor mv = cw.visitMethod(Opcodes.ACC_PUBLIC, funcName, desc, null, null); mv.visitCode(); Label methodStartLabel = new Label(); @@ -190,7 +193,7 @@ public void genJMethodWithBObjectMethodCall(BIRFunction func, ClassWriter cw, BI for (BIRNode.BIRFunctionParameter parameter : func.parameters) { instGen.generateVarLoad(mv, parameter, indexMap.addIfNotExists(parameter.name.value, parameter.type)); } - String methodDesc = JvmCodeGenUtil.getMethodDesc(func.type.paramTypes, retType, moduleClassName); + String methodDesc = JvmCodeGenUtil.getMethodDesc(typeEnv, func.type.paramTypes, retType, moduleClassName); mv.visitMethodInsn(INVOKESTATIC, splitClassName, encodedMethodName, methodDesc, false); Label methodEndLabel = new Label(); mv.visitLabel(methodEndLabel); @@ -248,9 +251,9 @@ public void genJMethodForBFunc(BIRFunction func, ClassWriter cw, BIRPackage modu BType retType = getReturnType(func); String desc; if (isObjectMethodSplit) { - desc = JvmCodeGenUtil.getMethodDesc(func.type.paramTypes, retType, moduleClassName); + desc = JvmCodeGenUtil.getMethodDesc(typeEnv, func.type.paramTypes, retType, moduleClassName); } else { - desc = JvmCodeGenUtil.getMethodDesc(func.type.paramTypes, retType); + desc = JvmCodeGenUtil.getMethodDesc(typeEnv, func.type.paramTypes, retType); } MethodVisitor mv = cw.visitMethod(access, funcName, desc, null, null); mv.visitCode(); @@ -336,8 +339,8 @@ private void handleParentModuleStart(MethodVisitor mv, PackageID packageID, Stri private BType getReturnType(BIRFunction func) { BType retType = func.type.retType; - if (JvmCodeGenUtil.isExternFunc(func) && Symbols.isFlagOn(retType.flags, Flags.PARAMETERIZED)) { - retType = JvmCodeGenUtil.UNIFIER.build(func.type.retType); + if (JvmCodeGenUtil.isExternFunc(func) && Symbols.isFlagOn(retType.getFlags(), Flags.PARAMETERIZED)) { + retType = JvmCodeGenUtil.UNIFIER.build(typeEnv, func.type.retType); } return retType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/ModuleStopMethodGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/ModuleStopMethodGen.java index 4ce3e4d86dd5..9789ce923cc1 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/ModuleStopMethodGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/methodgen/ModuleStopMethodGen.java @@ -28,7 +28,7 @@ import org.wso2.ballerinalang.compiler.bir.codegen.internal.AsyncDataCollector; import org.wso2.ballerinalang.compiler.bir.codegen.split.JvmConstantsGen; import org.wso2.ballerinalang.compiler.bir.model.BIRNode; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; +import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import java.util.Set; @@ -120,7 +120,7 @@ private void generateMethodBody(MethodVisitor mv, String initClass, String stopF JvmCodeGenUtil.createFunctionPointer(mv, initClass, stopFuncName); // no parent strand mv.visitInsn(ACONST_NULL); - jvmTypeGen.loadType(mv, new BNilType()); + jvmTypeGen.loadType(mv, BType.createNilType()); mv.visitLdcInsn("stop"); mv.visitInsn(ACONST_NULL); mv.visitIntInsn(BIPUSH, 1); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/optimizer/LargeMethodOptimizer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/optimizer/LargeMethodOptimizer.java index 579e108d25eb..52c0079e930a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/optimizer/LargeMethodOptimizer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/optimizer/LargeMethodOptimizer.java @@ -800,7 +800,7 @@ private void createNewFuncForPeriodicSplit(BIRFunction parentFunc, List memberTypes = new LinkedHashSet<>(2); memberTypes.add(newFuncReturnType); memberTypes.add(symbolTable.errorType); - return new BUnionType(null, memberTypes, false, false); + return new BUnionType(symbolTable.typeEnv(), null, memberTypes, false); } private BIRBasicBlock handleNewFuncReturnVal(BIRFunction function, BIROperand splitFuncCallResultOp, @@ -1666,7 +1666,7 @@ private BIRFunction createNewBIRFunctionAcrossBB(BIRFunction parentFunc, Name fu for (BIRVariableDcl funcArg : currSplit.funcArgs) { paramTypes.add(funcArg.type); } - BInvokableType type = new BInvokableType(paramTypes, retType, null); + BInvokableType type = new BInvokableType(symbolTable.typeEnv(), paramTypes, retType, null); BIRFunction birFunc = new BIRFunction(null, funcName, funcName, 0, type, DEFAULT_WORKER_NAME, 0, SymbolOrigin.VIRTUAL); @@ -1869,7 +1869,7 @@ private BIRFunction createNewBIRFuncForSplitInBB(Name funcName, BIRNonTerminator for (BIRVariableDcl funcArg : funcArgs) { paramTypes.add(funcArg.type); } - BInvokableType type = new BInvokableType(paramTypes, retType, null); + BInvokableType type = new BInvokableType(symbolTable.typeEnv(), paramTypes, retType, null); BIRFunction birFunc = new BIRFunction(null, funcName, funcName, 0, type, DEFAULT_WORKER_NAME, 0, SymbolOrigin.VIRTUAL); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/JvmAnnotationsGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/JvmAnnotationsGen.java index fb1305081f4e..e2cda9df5aa3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/JvmAnnotationsGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/JvmAnnotationsGen.java @@ -28,6 +28,7 @@ import org.wso2.ballerinalang.compiler.bir.codegen.JvmSignatures; import org.wso2.ballerinalang.compiler.bir.codegen.JvmTypeGen; import org.wso2.ballerinalang.compiler.bir.model.BIRNode; +import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; @@ -109,7 +110,7 @@ private int generateAnnotationsLoad(ClassWriter cw, List typeDefSet = new TreeSet<>(typeDefHashComparator); for (BIRTypeDefinition t : typeDefinitions) { - if (Symbols.isFlagOn(t.type.flags, Flags.ANONYMOUS)) { + if (Symbols.isFlagOn(t.type.getFlags(), Flags.ANONYMOUS)) { typeDefSet.add(t); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmArrayTypeGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmArrayTypeGen.java index f9e6631c66f9..cc67203f438c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmArrayTypeGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmArrayTypeGen.java @@ -84,7 +84,7 @@ public void createArrayType(MethodVisitor mv, BArrayType arrayType, Types types) if (TypeTags.isSimpleBasicType(arrayType.eType.tag)) { // Load the element type jvmTypeGen.loadType(mv, arrayType.eType); - int arraySize = arrayType.size; + int arraySize = arrayType.getSize(); mv.visitLdcInsn((long) arraySize); mv.visitInsn(L2I); @@ -96,7 +96,7 @@ public void createArrayType(MethodVisitor mv, BArrayType arrayType, Types types) mv.visitLdcInsn(jvmTypeGen.typeFlag(arrayType.eType)); - int arraySize = arrayType.size; + int arraySize = arrayType.getSize(); mv.visitLdcInsn((long) arraySize); mv.visitInsn(L2I); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmUnionTypeGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmUnionTypeGen.java index 27aa4abfa6d7..4c3bc12bcadb 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmUnionTypeGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/types/JvmUnionTypeGen.java @@ -109,7 +109,7 @@ public void createUnionType(MethodVisitor mv, BUnionType unionType) { jvmTypeGen.loadCyclicFlag(mv, unionType); - mv.visitLdcInsn(unionType.flags); + mv.visitLdcInsn(unionType.getFlags()); // initialize the union type without the members array if (nameLoaded) { mv.visitMethodInsn(INVOKESPECIAL, UNION_TYPE_IMPL, JVM_INIT_METHOD, diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/values/JvmObjectGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/values/JvmObjectGen.java index de5e9592c1c2..a87a368ecd65 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/values/JvmObjectGen.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/split/values/JvmObjectGen.java @@ -126,7 +126,7 @@ public void createAndSplitCallMethod(ClassWriter cw, List f String methodSig; // use index access, since retType can be nil. - methodSig = JvmCodeGenUtil.getMethodDesc(paramTypes, retType); + methodSig = JvmCodeGenUtil.getMethodDesc(jvmCastGen.typeEnv(), paramTypes, retType); // load self mv.visitVarInsn(ALOAD, 0); @@ -197,8 +197,8 @@ public void createAndSplitCallMethod(ClassWriter cw, List f } private boolean isListenerAttach(BIRNode.BIRFunction func) { - return func.name.value.equals("attach") && Symbols.isFlagOn(func.parameters.getFirst().type.flags, - Flags.SERVICE); + return func.name.value.equals("attach") && + Symbols.isFlagOn(func.parameters.getFirst().type.getFlags(), Flags.SERVICE); } public void createAndSplitGetMethod(ClassWriter cw, Map fields, String className, diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/emit/TypeEmitter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/emit/TypeEmitter.java index c1f634476a63..d8090ed0d1fb 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/emit/TypeEmitter.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/emit/TypeEmitter.java @@ -126,7 +126,7 @@ private static String emitParameterizedType(BParameterizedType type, int tabs) { } private static String emitTableType(BTableType bType, int tabs) { - boolean readonly = Symbols.isFlagOn(bType.flags, Flags.READONLY); + boolean readonly = Symbols.isFlagOn(bType.getFlags(), Flags.READONLY); if (bType.constraint == null) { return readonly ? bType.toString().concat(" & readonly") : bType.toString(); } @@ -240,8 +240,8 @@ private static String emitBInvokableType(BInvokableType bType, int tabs) { private static String emitBArrayType(BArrayType bType, int tabs) { String arrStr = emitTypeRef(bType.eType, 0); arrStr += "["; - if (bType.size > 0) { - arrStr += bType.size; + if (bType.getSize() > 0) { + arrStr += bType.getSize(); } arrStr += "]"; return arrStr; @@ -256,7 +256,7 @@ private static String emitBRecordType(BRecordType bType, int tabs) { for (BField bField : bType.fields.values()) { if (bField != null) { recordStr.append(emitTabs(tabs + 1)); - String flags = emitFlags(bField.type.flags); + String flags = emitFlags(bField.type.getFlags()); recordStr.append(flags); if (!flags.isEmpty()) { recordStr.append(emitSpaces(1)); @@ -273,7 +273,7 @@ private static String emitBRecordType(BRecordType bType, int tabs) { } private static String emitBObjectType(BObjectType bType, int tabs) { - boolean isService = (bType.flags & Flags.SERVICE) == Flags.SERVICE; + boolean isService = Symbols.isFlagOn(bType.getFlags(), Flags.SERVICE); StringBuilder str = new StringBuilder(); str.append(isService ? "service object" : "object"); @@ -283,7 +283,7 @@ private static String emitBObjectType(BObjectType bType, int tabs) { for (BField bField : bType.fields.values()) { if (bField != null) { str.append(emitTabs(tabs + 1)); - String flags = emitFlags(bField.type.flags); + String flags = emitFlags(bField.type.getFlags()); str.append(flags); if (!flags.isEmpty()) { str.append(emitSpaces(1)); @@ -366,21 +366,7 @@ private static String emitBTypeDesc(BTypedescType bType, int tabs) { } private static String emitBFiniteType(BFiniteType bType, int tabs) { - - StringBuilder str = new StringBuilder(); - str.append("["); - int i = 0; - int length = bType.getValueSpace().size(); - for (Object v : bType.getValueSpace()) { - str.append(v.toString()); - i += 1; - if (i < length) { - str.append(","); - str.append(emitSpaces(1)); - } - } - str.append("]"); - return str.toString(); + return "[" + bType.toString() + "]"; } private static String emitBTypeHandle(BHandleType bType, int tabs) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRBinaryWriter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRBinaryWriter.java index 8df6c69efb68..85d5593465c8 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRBinaryWriter.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRBinaryWriter.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.bir.writer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.ballerinalang.compiler.BLangCompilerException; @@ -49,16 +50,19 @@ */ public class BIRBinaryWriter { - private final ConstantPool cp = new ConstantPool(); + private final ConstantPool cp; private final BIRNode.BIRPackage birPackage; + private final Env typeEnv; - public BIRBinaryWriter(BIRNode.BIRPackage birPackage) { + public BIRBinaryWriter(BIRNode.BIRPackage birPackage, Env typeEnv) { this.birPackage = birPackage; + this.typeEnv = typeEnv; + cp = new ConstantPool(typeEnv); } public byte[] serialize() { ByteBuf birbuf = Unpooled.buffer(); - BIRTypeWriter typeWriter = new BIRTypeWriter(birbuf, cp); + BIRTypeWriter typeWriter = new BIRTypeWriter(birbuf, cp, typeEnv); // Write the package details in the form of constant pool entry @@ -391,7 +395,7 @@ private void writeAnnotation(ByteBuf buf, BIRTypeWriter typeWriter, BIRNode.BIRA } private void writeConstants(ByteBuf buf, List birConstList) { - BIRTypeWriter constTypeWriter = new BIRTypeWriter(buf, cp); + BIRTypeWriter constTypeWriter = new BIRTypeWriter(buf, cp, typeEnv); buf.writeInt(birConstList.size()); birConstList.forEach(constant -> writeConstant(buf, constTypeWriter, constant)); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRTypeWriter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRTypeWriter.java index c429eba1ff33..27beec2068be 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRTypeWriter.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/BIRTypeWriter.java @@ -17,17 +17,43 @@ */ package org.wso2.ballerinalang.compiler.bir.writer; +import io.ballerina.types.Atom; +import io.ballerina.types.AtomicType; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableDecimal; +import io.ballerina.types.EnumerableFloat; +import io.ballerina.types.EnumerableString; +import io.ballerina.types.Env; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.FunctionAtomicType; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.MappingAtomicType; +import io.ballerina.types.PredefinedTypeEnv; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.TypeAtom; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.CharStringSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.NonCharStringSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.StringSubtype; +import io.ballerina.types.subtypedata.XmlSubtype; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.ballerinalang.model.elements.MarkdownDocAttachment; import org.ballerinalang.model.symbols.SymbolKind; -import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.ByteCPEntry; -import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.FloatCPEntry; -import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.IntegerCPEntry; import org.wso2.ballerinalang.compiler.bir.writer.CPEntry.StringCPEntry; -import org.wso2.ballerinalang.compiler.semantics.analyzer.IsAnydataUniqueVisitor; -import org.wso2.ballerinalang.compiler.semantics.analyzer.IsPureTypeUniqueVisitor; -import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BAttachedFunction; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BConstantSymbol; @@ -45,7 +71,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -56,7 +81,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -73,13 +97,12 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.semantics.model.types.TypeFlags; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; -import org.wso2.ballerinalang.compiler.util.TypeTags; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.util.Flags; +import java.math.BigDecimal; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; @@ -89,29 +112,27 @@ /** * Writes bType to a Byte Buffer in binary format. * A ConstPool is used to store string typed information. - * + * * @since 0.995.0 */ -public class BIRTypeWriter implements TypeVisitor { +public class BIRTypeWriter extends TypeVisitor { private final ByteBuf buff; private final ConstantPool cp; - private static final IsPureTypeUniqueVisitor isPureTypeUniqueVisitor = new IsPureTypeUniqueVisitor(); - private static final IsAnydataUniqueVisitor isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(); + private final Set visitedAtoms = new HashSet<>(); + private final Env typeEnv; + private final PredefinedTypeEnv predefinedTypeEnv = PredefinedTypeEnv.getInstance(); - public BIRTypeWriter(ByteBuf buff, ConstantPool cp) { + public BIRTypeWriter(ByteBuf buff, ConstantPool cp, Env typeEnv) { this.buff = buff; this.cp = cp; + this.typeEnv = typeEnv; } public void visitType(BType type) { buff.writeByte(type.tag); buff.writeInt(addStringCPEntry(type.name.getValue())); - buff.writeLong(type.flags); - isPureTypeUniqueVisitor.reset(); - isAnydataUniqueVisitor.reset(); - buff.writeInt(TypeFlags.asMask(type.isNullable(), isAnydataUniqueVisitor.visit(type), - isPureTypeUniqueVisitor.visit(type))); + buff.writeLong(type.getFlags()); type.accept(this); } @@ -127,15 +148,10 @@ public void visit(BAnnotationType bAnnotationType) { @Override public void visit(BArrayType bArrayType) { buff.writeByte(bArrayType.state.getValue()); - buff.writeInt(bArrayType.size); + buff.writeInt(bArrayType.getSize()); writeTypeCpIndex(bArrayType.getElementType()); } - @Override - public void visit(BBuiltInRefType bBuiltInRefType) { - throwUnimplementedError(bBuiltInRefType); - } - @Override public void visit(BAnyType bAnyType) { } @@ -174,20 +190,15 @@ public void visit(BFiniteType bFiniteType) { BTypeSymbol tsymbol = bFiniteType.tsymbol; buff.writeInt(addStringCPEntry(tsymbol.name.value)); buff.writeLong(tsymbol.flags); - buff.writeInt(bFiniteType.getValueSpace().size()); - for (BLangExpression valueLiteral : bFiniteType.getValueSpace()) { - if (!(valueLiteral instanceof BLangLiteral bLangLiteral)) { - throw new AssertionError( - "Type serialization is not implemented for finite type with value: " + valueLiteral.getKind()); - } - writeTypeCpIndex(valueLiteral.getBType()); - writeValue(bLangLiteral.value, valueLiteral.getBType()); + buff.writeInt(bFiniteType.valueSpace.length); + for (SemNamedType semNamedType:bFiniteType.valueSpace) { + writeSemNamedType(semNamedType); } } @Override public void visit(BInvokableType bInvokableType) { - boolean isAnyFunction = Symbols.isFlagOn(bInvokableType.flags, Flags.ANY_FUNCTION); + boolean isAnyFunction = Symbols.isFlagOn(bInvokableType.getFlags(), Flags.ANY_FUNCTION); buff.writeBoolean(isAnyFunction); // write 1 if it’s an any function if not write 0 if (isAnyFunction) { @@ -305,7 +316,7 @@ public void visit(BNeverType bNeverType) { } @Override - public void visit(BNilType bNilType) { + public void visitNilType(BType bType) { // Nothing to do } @@ -494,11 +505,6 @@ private void writeAttachFunction(BAttachedFunction attachedFunc) { writeTypeCpIndex(attachedFunc.type); } - @Override - public void visit(BType bType) { - // Nothing to do - } - @Override public void visit(BXMLType bxmlType) { writeTypeCpIndex(bxmlType.constraint); @@ -562,63 +568,251 @@ private int addStringCPEntry(String value) { return cp.addCPEntry(new StringCPEntry(value)); } - private int addIntCPEntry(long value) { - return cp.addCPEntry(new IntegerCPEntry(value)); - } - - private int addFloatCPEntry(double value) { - return cp.addCPEntry(new FloatCPEntry(value)); - } - - private int addByteCPEntry(int value) { - return cp.addCPEntry(new ByteCPEntry(value)); - } - - private void writeValue(Object value, BType typeOfValue) { - ByteBuf byteBuf = Unpooled.buffer(); - switch (Types.getImpliedType(typeOfValue).tag) { - case TypeTags.INT: - case TypeTags.SIGNED32_INT: - case TypeTags.SIGNED16_INT: - case TypeTags.SIGNED8_INT: - case TypeTags.UNSIGNED32_INT: - case TypeTags.UNSIGNED16_INT: - case TypeTags.UNSIGNED8_INT: - byteBuf.writeInt(addIntCPEntry((Long) value)); - break; - case TypeTags.BYTE: - int byteValue = ((Number) value).intValue(); - byteBuf.writeInt(addByteCPEntry(byteValue)); - break; - case TypeTags.FLOAT: - // TODO:Remove the instanceof check by converting the float literal instance in Semantic analysis phase - double doubleVal = - value instanceof String ? Double.parseDouble((String) value) : ((Number) value).doubleValue(); - byteBuf.writeInt(addFloatCPEntry(doubleVal)); - break; - case TypeTags.STRING: - case TypeTags.CHAR_STRING: - case TypeTags.DECIMAL: - byteBuf.writeInt(addStringCPEntry(String.valueOf(value))); - break; - case TypeTags.BOOLEAN: - byteBuf.writeBoolean((Boolean) value); - break; - case TypeTags.NIL: - break; - default: - throw new UnsupportedOperationException("finite type value is not supported for type: " + typeOfValue); - } - - int length = byteBuf.nioBuffer().limit(); - buff.writeInt(length); - buff.writeBytes(byteBuf.nioBuffer().array(), 0, length); - } - private void writeTypeInclusions(List inclusions) { buff.writeInt(inclusions.size()); for (BType inclusion : inclusions) { writeTypeCpIndex(inclusion); } } + + private void writeNullableString(String nullableString) { + boolean hasNonNullString = nullableString != null; + buff.writeBoolean(hasNonNullString); + if (hasNonNullString) { + buff.writeInt(addStringCPEntry(nullableString)); + } + } + + private void writeSemNamedType(SemNamedType semNamedType) { + writeSemType(semNamedType.semType()); + writeNullableString(semNamedType.optName().orElse(null)); + } + + // --------------------------------------- Writing SemType ---------------------------------------------- + + private void writeSemType(SemType semType) { + boolean hasSemType = semType != null; + buff.writeBoolean(hasSemType); + if (!hasSemType) { + return; + } + + boolean isUniformTypeBitSet = semType instanceof BasicTypeBitSet; + buff.writeBoolean(isUniformTypeBitSet); + + buff.writeInt(semType.all()); + if (isUniformTypeBitSet) { + return; + } + + ComplexSemType complexSemType = (ComplexSemType) semType; + buff.writeInt(complexSemType.some()); + + ProperSubtypeData[] subtypeDataList = complexSemType.subtypeDataList(); + buff.writeByte(subtypeDataList.length); + for (ProperSubtypeData psd : subtypeDataList) { + writeProperSubtypeData(psd); + } + } + + private void writeProperSubtypeData(ProperSubtypeData psd) { + if (psd instanceof Bdd bdd) { + buff.writeByte(1); + writeBdd(bdd); + } else if (psd instanceof IntSubtype intSubtype) { + buff.writeByte(2); + writeIntSubtype(intSubtype); + } else if (psd instanceof BooleanSubtype booleanSubtype) { + buff.writeByte(3); + buff.writeBoolean(booleanSubtype.value); + } else if (psd instanceof FloatSubtype floatSubtype) { + buff.writeByte(4); + writeFloatSubtype(floatSubtype); + } else if (psd instanceof DecimalSubtype decimalSubtype) { + buff.writeByte(5); + writeDecimalSubtype(decimalSubtype); + } else if (psd instanceof StringSubtype stringSubtype) { + buff.writeByte(6); + writeStringSubtype(stringSubtype); + } else if (psd instanceof XmlSubtype xmlSubtype) { + buff.writeByte(7); + buff.writeInt(xmlSubtype.primitives); + writeBdd(xmlSubtype.sequence); + } else { + throw new IllegalStateException("Unknown ProperSubtypeData"); + } + } + + private void writeBdd(Bdd bdd) { + buff.writeBoolean(bdd instanceof BddNode); + if (bdd instanceof BddNode bddNode) { + writeBddNode(bddNode); + } else { + BddAllOrNothing bddAllOrNothing = (BddAllOrNothing) bdd; + buff.writeBoolean(bddAllOrNothing.isAll()); + } + } + + private static final byte REC_ATOM_KIND = 0; + private static final byte INLINED_ATOM_KIND = 1; + private static final byte TYPE_ATOM_KIND = 2; + + private void writeBddNode(BddNode bddNode) { + Atom atom = bddNode.atom(); + if (atom instanceof RecAtom recAtom) { + writeRecAtom(recAtom); + } else { + buff.writeByte(TYPE_ATOM_KIND); + TypeAtom typeAtom = (TypeAtom) atom; + visitedAtoms.add(typeAtom.getIdentifier()); + writeTypeAtom(typeAtom); + } + writeBdd(bddNode.left()); + writeBdd(bddNode.middle()); + writeBdd(bddNode.right()); + } + + private void writeRecAtom(RecAtom recAtom) { + if (shouldInline(recAtom)) { + writeInlinedRecAtom(recAtom); + } else { + buff.writeByte(REC_ATOM_KIND); + int index = typeEnv.compactRecIndex(recAtom); + buff.writeInt(index); + if (!predefinedTypeEnv.isPredefinedRecAtom(index)) { + buff.writeInt(recAtom.kind().ordinal()); + } + } + } + + private void writeInlinedRecAtom(RecAtom recAtom) { + visitedAtoms.add(recAtom.getIdentifier()); + buff.writeByte(INLINED_ATOM_KIND); + buff.writeInt(typeEnv.compactRecIndex(recAtom)); + TypeAtom typeAtom = switch (recAtom.kind()) { + case LIST_ATOM -> typeEnv.listAtom(typeEnv.listAtomType(recAtom)); + case FUNCTION_ATOM -> typeEnv.functionAtom(typeEnv.functionAtomType(recAtom)); + case MAPPING_ATOM -> typeEnv.mappingAtom(typeEnv.mappingAtomType(recAtom)); + case XML_ATOM, DISTINCT_ATOM -> + throw new IllegalStateException("Should not happen. Handled before reaching here"); + case CELL_ATOM -> throw new IllegalStateException("Cell atom cannot be recursive"); + }; + writeTypeAtom(typeAtom); + } + + private boolean shouldInline(RecAtom recAtom) { + // We can have cases where none of the BDDs have the actual BDD node in them just reference to it using + // RecAtoms. But when we deserialize the nodes we need to get the actual BDD node somehow. Currently, we + // "inline" the actual node first time we see it in the tree. Exceptions to this rule are predefined rec atoms + // which are unique and every environment has the same atoms and XML atoms + if (predefinedTypeEnv.isPredefinedRecAtom(recAtom.index) || recAtom.kind() == Atom.Kind.XML_ATOM || + recAtom.kind() == Atom.Kind.DISTINCT_ATOM) { + return false; + } + return !visitedAtoms.contains(recAtom.getIdentifier()); + } + + private void writeTypeAtom(TypeAtom typeAtom) { + buff.writeInt(typeAtom.index()); + writeAtomicType(typeAtom.atomicType()); + } + + private void writeAtomicType(AtomicType atomicType) { + if (atomicType instanceof MappingAtomicType mappingAtomicType) { + buff.writeByte(1); + writeMappingAtomicType(mappingAtomicType); + } else if (atomicType instanceof ListAtomicType listAtomicType) { + buff.writeByte(2); + writeListAtomicType(listAtomicType); + } else if (atomicType instanceof FunctionAtomicType functionAtomicType) { + buff.writeByte(3); + writeFunctionAtomicType(functionAtomicType); + } else if (atomicType instanceof CellAtomicType cellAtomicType) { + buff.writeByte(4); + writeSemType(cellAtomicType.ty()); + buff.writeByte(cellAtomicType.mut().ordinal()); + } else { + throw new UnsupportedOperationException("Unexpected atomic type " + atomicType); + } + } + + private void writeFunctionAtomicType(FunctionAtomicType functionAtomicType) { + writeSemType(functionAtomicType.paramType()); + writeSemType(functionAtomicType.retType()); + writeSemType(functionAtomicType.qualifiers()); + buff.writeBoolean(functionAtomicType.isGeneric()); + } + + private void writeMappingAtomicType(MappingAtomicType mat) { + String[] names = mat.names(); + buff.writeInt(names.length); + for (String name : names) { + buff.writeInt(addStringCPEntry(name)); + } + CellSemType[] types = mat.types(); + buff.writeInt(types.length); + for (CellSemType type : types) { + writeSemType(type); + } + writeSemType(mat.rest()); + } + + private void writeListAtomicType(ListAtomicType lat) { + FixedLengthArray fla = lat.members(); + List initial = fla.initial(); + buff.writeInt(initial.size()); + for (SemType type : initial) { + writeSemType(type); + } + buff.writeInt(fla.fixedLength()); + writeSemType(lat.rest()); + } + + private void writeIntSubtype(IntSubtype intSubtype) { + Range[] ranges = intSubtype.ranges; + buff.writeInt(ranges.length); + for (Range range : ranges) { + buff.writeLong(range.min); + buff.writeLong(range.max); + } + } + + private void writeFloatSubtype(FloatSubtype floatSubtype) { + buff.writeBoolean(floatSubtype.allowed); + buff.writeInt(floatSubtype.values.length); + for (EnumerableFloat ef : floatSubtype.values) { + buff.writeDouble(ef.value); + } + } + + private void writeDecimalSubtype(DecimalSubtype decimalSubtype) { + buff.writeBoolean(decimalSubtype.allowed); + buff.writeInt(decimalSubtype.values.length); + for (EnumerableDecimal ed : decimalSubtype.values) { + BigDecimal bigDecimal = ed.value; + buff.writeInt(bigDecimal.scale()); + byte[] unscaledValueBytes = bigDecimal.unscaledValue().toByteArray(); + buff.writeInt(unscaledValueBytes.length); + buff.writeBytes(unscaledValueBytes); + } + } + + private void writeStringSubtype(StringSubtype stringSubtype) { + CharStringSubtype charData = stringSubtype.getChar(); + buff.writeBoolean(charData.allowed); + EnumerableCharString[] charValues = charData.values; + buff.writeInt(charValues.length); + for (EnumerableCharString ecs : charValues) { + buff.writeInt(addStringCPEntry(ecs.value)); + } + NonCharStringSubtype nonCharData = stringSubtype.getNonChar(); + buff.writeBoolean(nonCharData.allowed); + EnumerableString[] nonCharValues = nonCharData.values; + buff.writeInt(nonCharValues.length); + for (EnumerableString ecs : nonCharValues) { + buff.writeInt(addStringCPEntry(ecs.value)); + } + } + + // --------------------------------------- End of SemType ----------------------------------------------- } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/ConstantPool.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/ConstantPool.java index bba3434f9d2a..edb6d2e08fc1 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/ConstantPool.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/writer/ConstantPool.java @@ -17,7 +17,7 @@ */ package org.wso2.ballerinalang.compiler.bir.writer; - +import io.ballerina.types.Env; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import org.ballerinalang.compiler.BLangCompilerException; @@ -45,6 +45,11 @@ public class ConstantPool { private final Map cpEntriesMap = new HashMap<>(); private final List cpEntries = new ArrayList<>(); + private final Env typeEnv; + + public ConstantPool(Env typeEnv) { + this.typeEnv = typeEnv; + } public int addCPEntry(CPEntry cpEntry) { int size = cpEntries.size(); @@ -118,7 +123,7 @@ private void writeToStream(DataOutputStream stream) throws IOException { case CP_ENTRY_SHAPE -> { CPEntry.ShapeCPEntry shapeCPEntry = (CPEntry.ShapeCPEntry) cpEntry; ByteBuf typeBuf = Unpooled.buffer(); - BIRTypeWriter birTypeWriter = new BIRTypeWriter(typeBuf, this); + BIRTypeWriter birTypeWriter = new BIRTypeWriter(typeBuf, this, typeEnv); birTypeWriter.visitType(shapeCPEntry.shape); byte[] bytes = Arrays.copyOfRange(typeBuf.array(), 0, typeBuf.writerIndex()); stream.writeInt(bytes.length); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ASTBuilderUtil.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ASTBuilderUtil.java index d36bb7fe379e..205071c02a21 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ASTBuilderUtil.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ASTBuilderUtil.java @@ -17,6 +17,7 @@ package org.wso2.ballerinalang.compiler.desugar; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -180,7 +181,7 @@ private static boolean isValueType(BType type) { static BLangExpression wrapToConversionExpr(BType sourceType, BLangExpression exprToWrap, SymbolTable symTable, Types types) { - if (types.isSameType(sourceType, exprToWrap.getBType()) || !isValueType(exprToWrap.getBType())) { + if (types.isSameTypeIncludingTags(sourceType, exprToWrap.getBType()) || !isValueType(exprToWrap.getBType())) { // No conversion needed. return exprToWrap; } @@ -329,7 +330,7 @@ static BLangReturn createReturnStmt(Location pos, BlockNode target) { public static BLangReturn createNilReturnStmt(Location pos, BType nilType) { final BLangReturn returnStmt = (BLangReturn) TreeBuilder.createReturnNode(); returnStmt.pos = pos; - returnStmt.expr = createLiteral(pos, nilType, Names.NIL_VALUE); + returnStmt.expr = createLiteral(pos, nilType, Names.NIL_VALUE.value); return returnStmt; } @@ -844,7 +845,7 @@ private static IdentifierNode createIdentifier(String value) { return node; } - public static BInvokableSymbol duplicateInvokableSymbol(BInvokableSymbol invokableSymbol) { + public static BInvokableSymbol duplicateInvokableSymbol(Env typeEnv, BInvokableSymbol invokableSymbol) { BInvokableSymbol dupFuncSymbol = Symbols.createFunctionSymbol(invokableSymbol.flags, invokableSymbol.name, invokableSymbol.originalName, invokableSymbol.pkgID, invokableSymbol.type, invokableSymbol.owner, @@ -864,18 +865,18 @@ public static BInvokableSymbol duplicateInvokableSymbol(BInvokableSymbol invokab new ArrayList<>((List) invokableSymbol.getAnnotations())); BInvokableType prevFuncType = (BInvokableType) invokableSymbol.type; - BInvokableType dupInvokableType = new BInvokableType(new ArrayList<>(prevFuncType.paramTypes), - prevFuncType.restType, prevFuncType.retType, - prevFuncType.tsymbol); + BInvokableType dupInvokableType = + new BInvokableType(typeEnv, List.copyOf(prevFuncType.paramTypes), + prevFuncType.restType, prevFuncType.retType, prevFuncType.tsymbol); if (Symbols.isFlagOn(invokableSymbol.flags, Flags.ISOLATED)) { dupFuncSymbol.flags |= Flags.ISOLATED; - dupInvokableType.flags |= Flags.ISOLATED; + dupInvokableType.addFlags(Flags.ISOLATED); } if (Symbols.isFlagOn(invokableSymbol.flags, Flags.TRANSACTIONAL)) { dupFuncSymbol.flags |= Flags.TRANSACTIONAL; - dupInvokableType.flags |= Flags.TRANSACTIONAL; + dupInvokableType.addFlags(Flags.TRANSACTIONAL); } dupFuncSymbol.type = dupInvokableType; @@ -884,7 +885,8 @@ public static BInvokableSymbol duplicateInvokableSymbol(BInvokableSymbol invokab return dupFuncSymbol; } - public static BInvokableSymbol duplicateFunctionDeclarationSymbol(BInvokableSymbol invokableSymbol, + public static BInvokableSymbol duplicateFunctionDeclarationSymbol(Env typeEnv, + BInvokableSymbol invokableSymbol, BSymbol owner, Name newName, PackageID newPkgID, @@ -914,9 +916,9 @@ public static BInvokableSymbol duplicateFunctionDeclarationSymbol(BInvokableSymb dupFuncSymbol.markdownDocumentation = invokableSymbol.markdownDocumentation; BInvokableType prevFuncType = (BInvokableType) invokableSymbol.type; - BType newFuncType = new BInvokableType(new ArrayList<>(prevFuncType.paramTypes), prevFuncType.restType, - prevFuncType.retType, prevFuncType.tsymbol); - newFuncType.flags |= prevFuncType.flags; + BType newFuncType = new BInvokableType(typeEnv, List.copyOf(prevFuncType.paramTypes), + prevFuncType.restType, prevFuncType.retType, prevFuncType.tsymbol); + newFuncType.addFlags(prevFuncType.getFlags()); dupFuncSymbol.type = newFuncType; return dupFuncSymbol; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/AnnotationDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/AnnotationDesugar.java index 79b4af59f064..28098fb1cee5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/AnnotationDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/AnnotationDesugar.java @@ -223,7 +223,7 @@ private void defineClassAnnotations(BLangPackage pkgNode, SymbolEnv env2, BLangF owner); if (lambdaFunction != null) { BType type = classDef.getBType(); - if (Symbols.isFlagOn(type.flags, Flags.OBJECT_CTOR)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.OBJECT_CTOR)) { if (normalMode) { // Add the lambda/invocation in a temporary block. BLangBlockStmt target = (BLangBlockStmt) TreeBuilder.createBlockNode(); @@ -503,7 +503,7 @@ private void defineFunctionAnnotations(BLangPackage pkgNode, SymbolEnv env, BLan int index; if (function.attachedFunction - && Symbols.isFlagOn(function.receiver.getBType().flags, Flags.OBJECT_CTOR)) { + && Symbols.isFlagOn(function.receiver.getBType().getFlags(), Flags.OBJECT_CTOR)) { addLambdaToGlobalAnnotMap(identifier, lambdaFunction, target); index = calculateIndex(initFnBody.stmts, function.receiver.getBType().tsymbol); } else { @@ -759,7 +759,7 @@ private void addVarArgsAnnotation(BLangFunction mainFunc, SymbolEnv env) { BLangListConstructorExpr.BLangArrayLiteral valueLiteral = (BLangListConstructorExpr.BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); - valueLiteral.setBType(new BArrayType(symTable.stringType)); + valueLiteral.setBType(new BArrayType(symTable.typeEnv(), symTable.stringType)); valueLiteral.typeChecked = true; valueLiteral.pos = pos; @@ -794,7 +794,7 @@ private void addVarArgsAnnotation(BLangFunction mainFunc, SymbolEnv env) { private BLangFunction defineFunction(Location pos, PackageID pkgID, BSymbol owner) { String funcName = ANNOT_FUNC + UNDERSCORE + annotFuncCount++; BLangFunction function = ASTBuilderUtil.createFunction(pos, funcName); - function.setBType(new BInvokableType(Collections.emptyList(), symTable.mapType, null)); + function.setBType(new BInvokableType(symTable.typeEnv(), Collections.emptyList(), symTable.mapType, null)); BLangBuiltInRefTypeNode anyMapType = (BLangBuiltInRefTypeNode) TreeBuilder.createBuiltInReferenceTypeNode(); anyMapType.typeKind = TypeKind.MAP; anyMapType.pos = pos; @@ -904,9 +904,9 @@ private BInvokableSymbol createInvokableSymbol(BLangFunction function, PackageID .toList(); functionSymbol.scope = new Scope(functionSymbol); functionSymbol.restParam = function.restParam != null ? function.restParam.symbol : null; - functionSymbol.type = new BInvokableType(Collections.emptyList(), + functionSymbol.type = new BInvokableType(symTable.typeEnv(), Collections.emptyList(), function.restParam != null ? function.restParam.getBType() : null, - new BMapType(TypeTags.MAP, symTable.anyType, null), + new BMapType(symTable.typeEnv(), TypeTags.MAP, symTable.anyType, null), null); function.symbol = functionSymbol; return functionSymbol; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java index 36c7f958a3b5..b83ce79d4df1 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureDesugar.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.desugar; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.symbols.SymbolKind; @@ -266,7 +267,7 @@ public void visit(BLangPackage pkgNode) { // Update function parameters. for (BLangFunction function : pkgNode.functions) { - updateFunctionParams(function); + updateFunctionParams(types.typeEnv(), function); } result = pkgNode; } @@ -369,10 +370,10 @@ private void addClosureMapToInit(BLangClassDefinition classDef, BVarSymbol mapSy initFunction.symbol.scope.define(paramSym.name, paramSym); initFunction.symbol.params.add(paramSym); BInvokableType initFuncSymbolType = (BInvokableType) initFunction.symbol.type; - initFuncSymbolType.paramTypes.add(mapSymbol.type); + initFuncSymbolType.addParamType(mapSymbol.type); BInvokableType initFnType = (BInvokableType) initFunction.getBType(); - initFnType.paramTypes.add(mapSymbol.type); + initFnType.addParamType(mapSymbol.type); BAttachedFunction attachedFunction = ((BObjectTypeSymbol) classDef.getBType().tsymbol).generatedInitializerFunc; attachedFunction.symbol.params.add(paramSym); @@ -644,21 +645,24 @@ private void createFunctionMap(BLangFunction funcNode, SymbolEnv funcEnv) { /** * Update the function parameters with closure parameter maps passed. * + * @param typeEnv type environment * @param funcNode function node */ - private static void updateFunctionParams(BLangFunction funcNode) { + private static void updateFunctionParams(Env typeEnv, BLangFunction funcNode) { // Add closure params to the required param list if there are any. - BInvokableSymbol dupFuncSymbol = ASTBuilderUtil.duplicateInvokableSymbol(funcNode.symbol); + BInvokableSymbol dupFuncSymbol = ASTBuilderUtil.duplicateInvokableSymbol(typeEnv, funcNode.symbol); funcNode.symbol = dupFuncSymbol; BInvokableType dupFuncType = (BInvokableType) dupFuncSymbol.type; int i = 0; + List newParamTypes = new ArrayList<>(dupFuncType.paramTypes); for (Map.Entry entry : funcNode.paramClosureMap.entrySet()) { BVarSymbol mapSymbol = entry.getValue(); dupFuncSymbol.params.add(i, mapSymbol); - dupFuncType.paramTypes.add(i, mapSymbol.type); + newParamTypes.add(i, mapSymbol.type); i++; } + dupFuncType.setParamTypes(newParamTypes); } /** diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java index 5d091d631269..26aff82289b0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ClosureGenerator.java @@ -655,9 +655,10 @@ private void updateFunctionParams(BLangFunction funcNode, List param VIRTUAL); funcSymbol.scope.define(symbolName, varSymbol); funcSymbol.params.add(varSymbol); - ((BInvokableType) funcSymbol.type).paramTypes.add(type); + BInvokableType funcType = (BInvokableType) funcSymbol.type; + funcType.addParamType(varSymbol.type); funcNode.requiredParams.add(ASTBuilderUtil.createVariable(pos, symbolName.value, type, null, - varSymbol)); + varSymbol)); } } @@ -728,7 +729,7 @@ private BLangFunction createFunction(String funcName, Location pos, PackageID pk function.flagSet.add(Flag.PUBLIC); BInvokableTypeSymbol invokableTypeSymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, Flags.PUBLIC, pkgID, bType, owner, pos, VIRTUAL); - function.setBType(new BInvokableType(new ArrayList<>(), bType, invokableTypeSymbol)); + function.setBType(new BInvokableType(symTable.typeEnv(), List.of(), bType, invokableTypeSymbol)); BLangBuiltInRefTypeNode typeNode = (BLangBuiltInRefTypeNode) TreeBuilder.createBuiltInReferenceTypeNode(); typeNode.setBType(bType); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java index 049701b1cd49..4b166c7b7b1a 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/Desugar.java @@ -723,8 +723,9 @@ protected void createInvokableSymbol(BLangFunction bLangFunction, SymbolEnv env) BType returnType = bLangFunction.returnTypeNode.getBType() == null ? symResolver.resolveTypeNode(bLangFunction.returnTypeNode, env) : bLangFunction.returnTypeNode.getBType(); - BInvokableType invokableType = new BInvokableType(new ArrayList<>(), getRestType(bLangFunction), - returnType, null); + BInvokableType invokableType = + new BInvokableType(symTable.typeEnv(), List.of(), getRestType(bLangFunction), + returnType, null); BInvokableSymbol functionSymbol = Symbols.createFunctionSymbol(Flags.asMask(bLangFunction.flagSet), new Name(bLangFunction.name.value), new Name(bLangFunction.name.originalValue), @@ -964,7 +965,7 @@ private List getConfigurableLangLibInvocationParam(BLangSimpleV BLangLiteral configNameLiteral = ASTBuilderUtil.createLiteral(configurableVar.pos, symTable.stringType, configVarName); BType type = configurableVar.getBType(); - BType typedescType = new BTypedescType(type, symTable.typeDesc.tsymbol); + BType typedescType = new BTypedescType(symTable.typeEnv(), type, symTable.typeDesc.tsymbol); BLangTypedescExpr typedescExpr = new BLangTypedescExpr(); typedescExpr.resolvedType = type; @@ -1379,7 +1380,7 @@ public void visit(BLangFunction funcNode) { // Duplicate the invokable symbol and the invokable type. funcNode.originalFuncSymbol = funcNode.symbol; - funcNode.symbol = ASTBuilderUtil.duplicateInvokableSymbol(funcNode.symbol); + funcNode.symbol = ASTBuilderUtil.duplicateInvokableSymbol(types.typeEnv(), funcNode.symbol); funcNode.requiredParams = rewrite(funcNode.requiredParams, funcEnv); funcNode.restParam = rewrite(funcNode.restParam, funcEnv); @@ -1715,7 +1716,7 @@ private void createVarDefStmts(BLangTupleVariable parentTupleVariable, BLangBloc if (variable.getKind() == NodeKind.TUPLE_VARIABLE) { // Else recursively create the var def statements. BLangTupleVariable tupleVariable = (BLangTupleVariable) variable; BLangIndexBasedAccess arrayAccessExpr = ASTBuilderUtil.createIndexBasesAccessExpr(tupleVariable.pos, - new BArrayType(symTable.anyType), tupleVarSymbol, indexExpr); + new BArrayType(symTable.typeEnv(), symTable.anyType), tupleVarSymbol, indexExpr); if (parentIndexAccessExpr != null) { arrayAccessExpr.expr = parentIndexAccessExpr; } @@ -1791,7 +1792,7 @@ private void createVarDefStmts(BLangRecordVariable parentRecordVariable, BLangBl if (recordFieldKeyValue.valueBindingPattern.getKind() == NodeKind.TUPLE_VARIABLE) { BLangTupleVariable tupleVariable = (BLangTupleVariable) recordFieldKeyValue.valueBindingPattern; BLangIndexBasedAccess arrayAccessExpr = ASTBuilderUtil.createIndexBasesAccessExpr(tupleVariable.pos, - new BArrayType(symTable.anyType), recordVarSymbol, indexExpr); + new BArrayType(symTable.typeEnv(), symTable.anyType), recordVarSymbol, indexExpr); if (parentIndexAccessExpr != null) { arrayAccessExpr.expr = parentIndexAccessExpr; } @@ -2024,12 +2025,12 @@ private BLangSimpleVariable generateRestFilter(BLangSimpleVarRef mapVarRef, Loca ASTBuilderUtil.createVariableRef(pos, mapVariable.symbol), typeCastExpr.getBType()); String entriesVarName = "$map$ref$entries$" + UNDERSCORE + restNum; BType constraintType = getRestFilterConstraintType(targetType); - BVarSymbol varSymbol = new BVarSymbol(constraintType.flags, null, null, constraintType, null, + BVarSymbol varSymbol = new BVarSymbol(constraintType.getFlags(), null, null, constraintType, null, null, null); BVarSymbol stringVarSymbol = new BVarSymbol(0, null, null, symTable.stringType, null, symTable.builtinPos, SymbolOrigin.VIRTUAL); - BType entriesType = new BMapType(TypeTags.MAP, - new BTupleType(Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + BType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, + new BTupleType(symTable.typeEnv(), Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), new BTupleMember(constraintType, varSymbol))), null); BLangSimpleVariable entriesInvocationVar = defVariable(pos, entriesType, parentBlockStmt, types.addConversionExprIfRequired(entriesInvocation, entriesType), @@ -2131,7 +2132,9 @@ private BLangLambdaFunction generateEntriesToMapLambda(Location pos, BType const .map(param -> param.symbol) .toList(); functionSymbol.scope = env.scope; - functionSymbol.type = new BInvokableType(Collections.singletonList(getStringAnyTupleType()), constraint, null); + functionSymbol.type = + new BInvokableType(symTable.typeEnv(), Collections.singletonList(getStringAnyTupleType()), constraint, + null); function.symbol = functionSymbol; rewrite(function, env); env.enclPkg.addFunction(function); @@ -2282,7 +2285,7 @@ private BLangInvocation generateErrorCauseLanglibFunction(Location pos, BType ca private BLangInvocation generateCreateRecordValueInvocation(Location pos, BType targetType, BVarSymbol source) { - BType typedescType = new BTypedescType(targetType, symTable.typeDesc.tsymbol); + BType typedescType = new BTypedescType(symTable.typeEnv(), targetType, symTable.typeDesc.tsymbol); BLangInvocation invocationNode = createInvocationNode(CREATE_RECORD_VALUE, new ArrayList<>(), typedescType); BLangTypedescExpr typedescExpr = new BLangTypedescExpr(); @@ -2293,14 +2296,14 @@ private BLangInvocation generateCreateRecordValueInvocation(Location pos, invocationNode.symbol = symResolver.lookupLangLibMethod(typedescType, Names.fromString(CREATE_RECORD_VALUE), env); invocationNode.requiredArgs = Lists.of(ASTBuilderUtil.createVariableRef(pos, source), typedescExpr); - invocationNode.setBType(BUnionType.create(null, targetType, symTable.errorType)); + invocationNode.setBType(BUnionType.create(symTable.typeEnv(), null, targetType, symTable.errorType)); return invocationNode; } private BLangInvocation generateCloneWithTypeInvocation(Location pos, BType targetType, BVarSymbol source) { - BType typedescType = new BTypedescType(targetType, symTable.typeDesc.tsymbol); + BType typedescType = new BTypedescType(symTable.typeEnv(), targetType, symTable.typeDesc.tsymbol); BLangInvocation invocationNode = createInvocationNode(CLONE_WITH_TYPE, new ArrayList<>(), typedescType); BLangTypedescExpr typedescExpr = new BLangTypedescExpr(); @@ -2310,7 +2313,7 @@ private BLangInvocation generateCloneWithTypeInvocation(Location pos, invocationNode.expr = typedescExpr; invocationNode.symbol = symResolver.lookupLangLibMethod(typedescType, Names.fromString(CLONE_WITH_TYPE), env); invocationNode.requiredArgs = Lists.of(ASTBuilderUtil.createVariableRef(pos, source), typedescExpr); - invocationNode.setBType(BUnionType.create(null, targetType, symTable.errorType)); + invocationNode.setBType(BUnionType.create(symTable.typeEnv(), null, targetType, symTable.errorType)); return invocationNode; } @@ -2417,7 +2420,7 @@ private BInvokableSymbol createReturnTrueStatement(Location pos, BLangFunction f .map(param -> param.symbol) .toList(); functionSymbol.scope = env.scope; - functionSymbol.type = new BInvokableType(Collections.singletonList(getStringAnyTupleType()), + functionSymbol.type = new BInvokableType(symTable.typeEnv(), Collections.singletonList(getStringAnyTupleType()), getRestType(functionSymbol), symTable.booleanType, null); function.symbol = functionSymbol; rewrite(function, env); @@ -2449,7 +2452,7 @@ private BTupleType getStringAnyTupleType() { add(new BTupleMember(symTable.stringType, stringVarSymbol)); add(new BTupleMember(symTable.anyType, anyVarSymbol)); }}; - return new BTupleType(typeList); + return new BTupleType(symTable.typeEnv(), typeList); } /** @@ -2538,7 +2541,7 @@ public void visit(BLangTupleDestructure tupleDestructure) { final BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(tupleDestructure.pos); //create a array of any-type based on the dimension - BType runTimeType = new BArrayType(symTable.anyType); + BType runTimeType = new BArrayType(symTable.typeEnv(), symTable.anyType); //create a simple var for the array 'any[] x = (tuple)' based on the dimension for x String name = "tuple"; @@ -2687,7 +2690,7 @@ private void createVarRefAssignmentStmts(BLangTupleVarRef parentTupleVariable, B BLangTupleVarRef tupleVarRef = (BLangTupleVarRef) expression; BLangLiteral indexExpr = ASTBuilderUtil.createLiteral(tupleVarRef.pos, symTable.intType, (long) index); BLangIndexBasedAccess arrayAccessExpr = ASTBuilderUtil.createIndexBasesAccessExpr(tupleVarRef.pos, - new BArrayType(symTable.anyType), tupleVarSymbol, indexExpr); + new BArrayType(symTable.typeEnv(), symTable.anyType), tupleVarSymbol, indexExpr); if (parentIndexAccessExpr != null) { arrayAccessExpr.expr = parentIndexAccessExpr; } @@ -2793,7 +2796,7 @@ public void visit(BLangRecordDestructure recordDestructure) { final BLangBlockStmt blockStmt = ASTBuilderUtil.createBlockStmt(recordDestructure.pos); - BType runTimeType = new BMapType(TypeTags.MAP, symTable.anyType, null); + BType runTimeType = new BMapType(symTable.typeEnv(), TypeTags.MAP, symTable.anyType, null); String name = "$map$_0"; final BLangSimpleVariable mapVariable = @@ -4161,9 +4164,9 @@ private BLangExpression createConditionForErrorArgListBindingPattern(BLangErrorB symTable.stringType, null, symTable.builtinPos, VIRTUAL); BVarSymbol anydataVarSymbol = new BVarSymbol(0, null, null, symTable.anydataType, null, symTable.builtinPos, VIRTUAL); - BMapType entriesType = new BMapType(TypeTags.MAP, new BTupleType(Arrays.asList( - new BTupleMember(symTable.stringType, stringVarSymbol), - new BTupleMember(symTable.anydataType, anydataVarSymbol))), null); + BMapType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, new BTupleType(symTable.typeEnv(), + Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + new BTupleMember(symTable.anydataType, anydataVarSymbol))), null); BLangInvocation entriesInvocation = generateMapEntriesInvocation(errorDetailVarRef, entriesType); BLangSimpleVariableDef entriesVarDef = createVarDef("$entries$", entriesType, entriesInvocation, restPatternPos); @@ -4336,12 +4339,13 @@ private void createRestPattern(Location pos, List keysToRemove, BLangSim BType targetType, BLangBlockStmt blockStmt, BLangSimpleVarRef restMatchPatternVarRef) { BType constraintType = getRestFilterConstraintType(targetType); - BVarSymbol varSymbol = new BVarSymbol(constraintType.flags, null, null, constraintType, null, + BVarSymbol varSymbol = new BVarSymbol(constraintType.getFlags(), null, null, constraintType, null, null, null); BVarSymbol stringVarSymbol = new BVarSymbol(0, null, null, symTable.stringType, null, symTable.builtinPos, VIRTUAL); - BMapType entriesType = new BMapType(TypeTags.MAP, new BTupleType(Arrays.asList(new BTupleMember( - symTable.stringType, stringVarSymbol), new BTupleMember(constraintType, varSymbol))), null); + BMapType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, new BTupleType(symTable.typeEnv(), + Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + new BTupleMember(constraintType, varSymbol))), null); BLangInvocation entriesInvocation = generateMapEntriesInvocation(matchExprVarRef, entriesType); BLangSimpleVariableDef entriesVarDef = createVarDef("$entries$", entriesType, entriesInvocation, pos); blockStmt.addStatement(entriesVarDef); @@ -4531,9 +4535,9 @@ private void createRestBindingPatternCondition(BLangMappingBindingPattern mappin BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(restType); BVarSymbol stringVarSymbol = new BVarSymbol(0, null, null, symTable.stringType, null, symTable.builtinPos, VIRTUAL); - BMapType entriesType = new BMapType(TypeTags.MAP, new BTupleType(Arrays.asList( - new BTupleMember(symTable.stringType, stringVarSymbol), - new BTupleMember(restType, varSymbol))), null); + BMapType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, new BTupleType(symTable.typeEnv(), + Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + new BTupleMember(restType, varSymbol))), null); BLangInvocation entriesInvocation = generateMapEntriesInvocation(varRef, entriesType); BLangSimpleVariableDef entriesVarDef = createVarDef("$entries$", entriesType, entriesInvocation, restPatternPos); @@ -4649,12 +4653,12 @@ private BLangExpression createVarCheckConditionForMappingMatchPattern(BLangMappi Location restPatternPos = restMatchPattern.pos; List keysToRemove = getKeysToRemove(mappingMatchPattern); BType restType = ((BRecordType) restMatchPattern.getBType()).restFieldType; - BVarSymbol varSymbol = new BVarSymbol(restType.flags, null, null, restType, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(restType.getFlags(), null, null, restType, null, null, null); BVarSymbol stringVarSymbol = new BVarSymbol(0, null, null, symTable.stringType, null, symTable.builtinPos, VIRTUAL); - BMapType entriesType = new BMapType(TypeTags.MAP, new BTupleType(Arrays.asList( - new BTupleMember(symTable.stringType, stringVarSymbol), - new BTupleMember(restType, varSymbol))), null); + BMapType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, new BTupleType(symTable.typeEnv(), + Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + new BTupleMember(restType, varSymbol))), null); BLangInvocation entriesInvocation = generateMapEntriesInvocation(tempCastVarRef, entriesType); BLangSimpleVariableDef entriesVarDef = createVarDef("$entries$", entriesType, entriesInvocation, restPatternPos); @@ -4827,9 +4831,9 @@ private BLangExpression createConditionForErrorArgListMatchPattern(BLangErrorMat symTable.stringType, null, symTable.builtinPos, VIRTUAL); BVarSymbol anydataVarSymbol = new BVarSymbol(0, null, null, symTable.anydataType, null, symTable.builtinPos, VIRTUAL); - BMapType entriesType = new BMapType(TypeTags.MAP, new BTupleType(Arrays.asList( - new BTupleMember(symTable.stringType, stringVarSymbol), - new BTupleMember(symTable.anydataType, anydataVarSymbol))), null); + BMapType entriesType = new BMapType(symTable.typeEnv(), TypeTags.MAP, new BTupleType(symTable.typeEnv(), + Arrays.asList(new BTupleMember(symTable.stringType, stringVarSymbol), + new BTupleMember(symTable.anydataType, anydataVarSymbol))), null); BLangInvocation entriesInvocation = generateMapEntriesInvocation(errorDetailVarRef, entriesType); BLangSimpleVariableDef entriesVarDef = createVarDef("$entries$", entriesType, entriesInvocation, restPatternPos); @@ -5336,7 +5340,7 @@ private BLangFail createOnFailInvocation(BLangOnFailClause onFailClause, BLangFa } } - BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(fail.pos, symTable.nilType, Names.NIL_VALUE); + BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(fail.pos, symTable.nilType, Names.NIL_VALUE.value); BLangStatementExpression statementExpression = createStatementExpression(onFailBody, nilLiteral); statementExpression.setBType(symTable.nilType); @@ -5597,8 +5601,8 @@ public void visit(BLangLock lockNode) { enclLocks.push(lockStmt); - BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(lockNode.pos, symTable.nilType, Names.NIL_VALUE); - BType nillableError = BUnionType.create(null, symTable.errorType, symTable.nilType); + BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(lockNode.pos, symTable.nilType, Names.NIL_VALUE.value); + BType nillableError = BUnionType.create(symTable.typeEnv(), null, symTable.errorType, symTable.nilType); BLangStatementExpression statementExpression = createStatementExpression(lockNode.body, nilLiteral); statementExpression.setBType(symTable.nilType); @@ -5906,7 +5910,7 @@ private BLangOnFailClause createRetryInternalOnFail(Location pos, BLangUnaryExpr createNotBinaryExpression(Location pos, BLangExpression expression) { List paramTypes = new ArrayList<>(); paramTypes.add(symTable.booleanType); - BInvokableType type = new BInvokableType(paramTypes, symTable.booleanType, + BInvokableType type = new BInvokableType(symTable.typeEnv(), paramTypes, symTable.booleanType, null); BOperatorSymbol notOperatorSymbol = new BOperatorSymbol( Names.fromString(OperatorKind.NOT.value()), symTable.rootPkgSymbol.pkgID, type, symTable.rootPkgSymbol, @@ -5933,7 +5937,7 @@ BLangLambdaFunction createLambdaFunction(Location pos, String functionNamePrefix lambdaFunction.pos = pos; List paramTypes = new ArrayList<>(); lambdaFunctionVariable.forEach(variable -> paramTypes.add(variable.symbol.type)); - lambdaFunction.setBType(new BInvokableType(paramTypes, func.symbol.type.getReturnType(), + lambdaFunction.setBType(new BInvokableType(symTable.typeEnv(), paramTypes, func.symbol.type.getReturnType(), null)); return lambdaFunction; } @@ -5991,7 +5995,8 @@ public void visit(BLangListConstructorExpr listConstructor) { expr = new BLangTupleLiteral(listConstructor.pos, listConstructor.exprs, listConstructor.getBType()); result = rewriteExpr(expr); } else if (listConstructorType.tag == TypeTags.JSON) { - expr = new BLangJSONArrayLiteral(listConstructor.exprs, new BArrayType(listConstructor.getBType())); + expr = new BLangJSONArrayLiteral(listConstructor.exprs, + new BArrayType(symTable.typeEnv(), listConstructor.getBType())); result = rewriteExpr(expr); } else if (getElementType(listConstructorType).tag == TypeTags.JSON) { expr = new BLangJSONArrayLiteral(listConstructor.exprs, listConstructor.getBType()); @@ -6018,7 +6023,8 @@ public void visit(BLangArrayLiteral arrayLiteral) { arrayLiteral.exprs = rewriteExprs(arrayLiteral.exprs); BType arrayLiteralType = Types.getImpliedType(arrayLiteral.getBType()); if (arrayLiteralType.tag == TypeTags.JSON) { - result = new BLangJSONArrayLiteral(arrayLiteral.exprs, new BArrayType(arrayLiteral.getBType())); + result = new BLangJSONArrayLiteral(arrayLiteral.exprs, + new BArrayType(symTable.typeEnv(), arrayLiteral.getBType())); return; } else if (getElementType(arrayLiteralType).tag == TypeTags.JSON) { result = new BLangJSONArrayLiteral(arrayLiteral.exprs, arrayLiteral.getBType()); @@ -6050,8 +6056,8 @@ public void visit(BLangTupleLiteral tupleLiteral) { spreadOpType = Types.getImpliedType(spreadOpType); if (spreadOpType.tag == TypeTags.ARRAY) { BArrayType spreadOpBArray = (BArrayType) spreadOpType; - if (spreadOpBArray.size >= 0) { - i += spreadOpBArray.size; + if (spreadOpBArray.getSize() >= 0) { + i += spreadOpBArray.getSize(); continue; } } else { @@ -6166,7 +6172,7 @@ private void generateFieldsForUserUnspecifiedRecordFields(List fieldNames = getNamesOfUserSpecifiedRecordFields(userSpecifiedFields); Location pos = recordLiteral.pos; BRecordType recordType = (BRecordType) type; - boolean isReadonly = Symbols.isFlagOn(recordType.flags, Flags.READONLY); + boolean isReadonly = Symbols.isFlagOn(recordType.getFlags(), Flags.READONLY); generateFieldsForUserUnspecifiedRecordFields(recordType, userSpecifiedFields, fieldNames, pos, isReadonly); } @@ -6563,7 +6569,8 @@ private BLangStatementExpression rewriteLaxMapAccess(BLangFieldBasedAccess field BLangStatementExpression statementExpression = new BLangStatementExpression(); BLangBlockStmt block = new BLangBlockStmt(); statementExpression.stmt = block; - BUnionType fieldAccessType = BUnionType.create(null, fieldAccessExpr.getBType(), symTable.errorType); + BUnionType fieldAccessType = + BUnionType.create(symTable.typeEnv(), null, fieldAccessExpr.getBType(), symTable.errorType); Location pos = fieldAccessExpr.pos; BLangSimpleVariableDef result = createVarDef("$mapAccessResult$", fieldAccessType, null, pos); block.addStatement(result); @@ -6577,7 +6584,7 @@ private BLangStatementExpression rewriteLaxMapAccess(BLangFieldBasedAccess field BLangLiteral mapIndex = ASTBuilderUtil.createLiteral( fieldAccessExpr.field.pos, symTable.stringType, fieldAccessExpr.field.value); BLangMapAccessExpr mapAccessExpr = new BLangMapAccessExpr(pos, fieldAccessExpr.expr, mapIndex); - BUnionType xmlOrNil = BUnionType.create(null, fieldAccessExpr.getBType(), symTable.nilType); + BUnionType xmlOrNil = BUnionType.create(symTable.typeEnv(), null, fieldAccessExpr.getBType(), symTable.nilType); mapAccessExpr.setBType(xmlOrNil); BLangSimpleVariableDef mapResult = createVarDef("$mapAccess", xmlOrNil, mapAccessExpr, pos); BLangSimpleVarRef mapResultRef = ASTBuilderUtil.createVariableRef(pos, mapResult.var.symbol); @@ -6688,7 +6695,7 @@ public void visit(BLangIndexBasedAccess indexAccessExpr) { if (varRefType.tag == TypeTags.MAP) { targetVarRef = new BLangMapAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr, indexAccessExpr.indexExpr, indexAccessExpr.isStoreOnCreation); - } else if (types.isSubTypeOfMapping(types.getSafeType(varRefType, true, false))) { + } else if (types.isSubTypeOfMapping(types.getNilLiftType(varRefType.semType()))) { targetVarRef = new BLangStructFieldAccessExpr(indexAccessExpr.pos, indexAccessExpr.expr, indexAccessExpr.indexExpr, (BVarSymbol) indexAccessExpr.symbol, false); @@ -6867,7 +6874,7 @@ public void visit(BLangInvocation.BLangActionInvocation actionInvocation) { } // Add `@strand {thread: "any"}` annotation to an isolated start-action. - if (actionInvocation.async && Symbols.isFlagOn(actionInvocation.symbol.type.flags, Flags.ISOLATED)) { + if (actionInvocation.async && Symbols.isFlagOn(actionInvocation.symbol.type.getFlags(), Flags.ISOLATED)) { addStrandAnnotationWithThreadAny(actionInvocation.pos); actionInvocation.addAnnotationAttachment(this.strandAnnotAttachement); ((BInvokableSymbol) actionInvocation.symbol) @@ -7042,7 +7049,8 @@ private BLangInvocation createInvocationForPathParams( BResourcePathSegmentSymbol lastPathSegmentSym = pathSegmentSymbols.get(pathSegmentSymbols.size() - 1); if (lastPathSegmentSym.kind == SymbolKind.RESOURCE_PATH_REST_PARAM_SEGMENT) { invokableSymbol.restParam = new BVarSymbol(0, Names.EMPTY, this.env.scope.owner.pkgID, - new BArrayType(lastPathSegmentSym.type), this.env.scope.owner, lastPathSegmentSym.pos, VIRTUAL); + new BArrayType(symTable.typeEnv(), lastPathSegmentSym.type), this.env.scope.owner, + lastPathSegmentSym.pos, VIRTUAL); pathSegmentCount--; } @@ -7099,9 +7107,10 @@ private BLangExpression rewriteInvocation(BLangInvocation invocation, boolean as result = invRef; BInvokableSymbol invSym = (BInvokableSymbol) invocation.symbol; - if (Symbols.isFlagOn(invSym.retType.flags, Flags.PARAMETERIZED)) { - BType retType = unifier.build(invSym.retType); - invocation.setBType(invocation.async ? new BFutureType(TypeTags.FUTURE, retType, null) : retType); + if (Symbols.isFlagOn(invSym.retType.getFlags(), Flags.PARAMETERIZED)) { + BType retType = unifier.build(symTable.typeEnv(), invSym.retType); + invocation.setBType(invocation.async ? + new BFutureType(symTable.typeEnv(), retType, null) : retType); } if (invocation.expr == null) { @@ -7136,7 +7145,7 @@ private BLangExpression rewriteInvocation(BLangInvocation invocation, boolean as private void populateOCEInvocation(BLangInvocation invocation, BLangInvocation invRef) { - if (invocation.objectInitMethod && Symbols.isFlagOn(invocation.expr.getBType().flags, Flags.OBJECT_CTOR)) { + if (invocation.objectInitMethod && Symbols.isFlagOn(invocation.expr.getBType().getFlags(), Flags.OBJECT_CTOR)) { BObjectType initializingObject = (BObjectType) invocation.expr.getBType(); BLangClassDefinition classDef = initializingObject.classDef; if (classDef.hasClosureVars) { @@ -7305,13 +7314,13 @@ private BLangInvocation desugarStreamTypeInit(BLangTypeInit typeInitExpr) { BStreamType referredStreamType = (BStreamType) Types.getImpliedType(typeInitExpr.getBType()); BType constraintType = referredStreamType.constraint; - BType constraintTdType = new BTypedescType(constraintType, symTable.typeDesc.tsymbol); + BType constraintTdType = new BTypedescType(symTable.typeEnv(), constraintType, symTable.typeDesc.tsymbol); BLangTypedescExpr constraintTdExpr = new BLangTypedescExpr(); constraintTdExpr.resolvedType = constraintType; constraintTdExpr.setBType(constraintTdType); BType completionType = referredStreamType.completionType; - BType completionTdType = new BTypedescType(completionType, symTable.typeDesc.tsymbol); + BType completionTdType = new BTypedescType(symTable.typeEnv(), completionType, symTable.typeDesc.tsymbol); BLangTypedescExpr completionTdExpr = new BLangTypedescExpr(); completionTdExpr.resolvedType = completionType; completionTdExpr.setBType(completionTdType); @@ -7322,7 +7331,8 @@ private BLangInvocation desugarStreamTypeInit(BLangTypeInit typeInitExpr) { } BLangInvocation streamConstructInvocation = ASTBuilderUtil.createInvocationExprForMethod( typeInitExpr.pos, symbol, args, symResolver); - streamConstructInvocation.setBType(new BStreamType(TypeTags.STREAM, constraintType, completionType, null)); + streamConstructInvocation.setBType(new BStreamType(symTable.typeEnv(), TypeTags.STREAM, constraintType, + completionType, null)); return streamConstructInvocation; } @@ -7715,7 +7725,7 @@ private void createTypeCastExprForArithmeticExpr(BLangBinaryExpr binaryExpr, int return; } if (TypeTags.isXMLTypeTag(lhsExprTypeTag) && !TypeTags.isXMLTypeTag(rhsExprTypeTag)) { - if (types.checkTypeContainString(binaryExpr.rhsExpr.getBType())) { + if (types.isStringSubType(binaryExpr.rhsExpr.getBType())) { binaryExpr.rhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.rhsExpr, binaryExpr.rhsExpr.pos, symTable.xmlType); return; @@ -7724,7 +7734,7 @@ private void createTypeCastExprForArithmeticExpr(BLangBinaryExpr binaryExpr, int return; } if (TypeTags.isXMLTypeTag(rhsExprTypeTag) && !TypeTags.isXMLTypeTag(lhsExprTypeTag)) { - if (types.checkTypeContainString(binaryExpr.lhsExpr.getBType())) { + if (types.isStringSubType(binaryExpr.lhsExpr.getBType())) { binaryExpr.lhsExpr = ASTBuilderUtil.createXMLTextLiteralNode(binaryExpr, binaryExpr.lhsExpr, binaryExpr.rhsExpr.pos, symTable.xmlType); return; @@ -7848,7 +7858,7 @@ private BLangExpression addNilType(BType exprType, BLangExpression expr) { LinkedHashSet members = new LinkedHashSet<>(2); members.add(exprType); members.add(symTable.nilType); - BUnionType unionType = new BUnionType(null, members, true, false); + BUnionType unionType = new BUnionType(types.typeEnv(), null, members, false); return createTypeCastExpr(expr, unionType); } @@ -8111,8 +8121,8 @@ public void visit(BLangLambdaFunction bLangLambdaFunction) { bLangLambdaFunction.function = rewrite(bLangLambdaFunction.function, bLangLambdaFunction.capturedClosureEnv); BLangFunction function = bLangLambdaFunction.function; // Add `@strand {thread: "any"}` annotation to an isolated named worker declaration in an isolated function. - if (function.flagSet.contains(Flag.WORKER) && Symbols.isFlagOn(function.symbol.type.flags, Flags.ISOLATED) && - Symbols.isFlagOn(env.enclInvokable.symbol.flags, Flags.ISOLATED)) { + if (function.flagSet.contains(Flag.WORKER) && Symbols.isFlagOn(function.symbol.type.getFlags(), Flags.ISOLATED) + && Symbols.isFlagOn(env.enclInvokable.symbol.flags, Flags.ISOLATED)) { addStrandAnnotationWithThreadAny(function.pos); function.addAnnotationAttachment(this.strandAnnotAttachement); BInvokableSymbol funcSymbol = function.symbol; @@ -8170,8 +8180,8 @@ public void visit(BLangArrowFunction bLangArrowFunction) { funcSymbol.retType = funcNode.returnTypeNode.getBType(); // Create function type. List paramTypes = new ArrayList<>(paramSymbols.stream().map(paramSym -> paramSym.type).toList()); - funcNode.setBType(new BInvokableType(paramTypes, getRestType(funcSymbol), funcNode.returnTypeNode.getBType(), - funcSymbol.type.tsymbol)); + funcNode.setBType(new BInvokableType(symTable.typeEnv(), paramTypes, getRestType(funcSymbol), + funcNode.returnTypeNode.getBType(), funcSymbol.type.tsymbol)); lambdaFunction.function.pos = bLangArrowFunction.pos; lambdaFunction.function.body.pos = bLangArrowFunction.pos; @@ -8357,7 +8367,7 @@ private BLangClassDefinition desugarTemplateLiteralObjectTypedef(List(); - expr.setBType(new BArrayType(symTable.anyType)); + expr.setBType(new BArrayType(symTable.typeEnv(), symTable.anyType)); return expr; } @@ -9334,7 +9343,7 @@ private BLangLiteral createByteLiteral(Location pos, Byte value) { } private BLangExpression createTypeCastExpr(BLangExpression expr, BType targetType) { - if (types.isSameType(expr.getBType(), targetType)) { + if (types.isSameTypeIncludingTags(expr.getBType(), targetType)) { return expr; } @@ -9542,7 +9551,7 @@ private void reorderArguments(BLangInvocation iExpr) { if (refType.tag == TypeTags.ARRAY) { BArrayType arrayType = (BArrayType) refType; if (arrayType.state == BArrayState.CLOSED && - arrayType.size == (iExpr.requiredArgs.size() - originalRequiredArgCount)) { + arrayType.getSize() == (iExpr.requiredArgs.size() - originalRequiredArgCount)) { // If the array was a closed array that provided only for the non rest params, set the rest param // type as the element type to satisfy code gen. The foreach will not be executed at runtime. valueExpr.setBType(restParamType.eType); @@ -9806,7 +9815,7 @@ private BType getStructuredBindingPatternType(BLangVariable bindingPatternVariab memberTypes.add( new BTupleMember(member, varSymbol)); } - BTupleType tupleType = new BTupleType(memberTypes); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), memberTypes); if (tupleVariable.restVariable != null) { BArrayType restArrayType = (BArrayType) getStructuredBindingPatternType(tupleVariable.restVariable); tupleType.restType = restArrayType.eType; @@ -9840,7 +9849,7 @@ private BType getStructuredBindingPatternType(BLangVariable bindingPatternVariab recordSymbol.scope.define(fieldName, fieldSymbol); } - BRecordType recordVarType = new BRecordType(recordSymbol); + BRecordType recordVarType = new BRecordType(symTable.typeEnv(), recordSymbol); recordVarType.fields = fields; // if rest param is null we treat it as an open record with anydata rest param @@ -9877,7 +9886,7 @@ private BType getStructuredBindingPatternType(BLangVariable bindingPatternVariab TypeDefBuilderHelper.createTypeDefinitionForTSymbol(detailType, detailType.tsymbol, recordTypeNode, env); } - BErrorType errorType = new BErrorType(errorTypeSymbol, detailType); + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorTypeSymbol, detailType); errorTypeSymbol.type = errorType; TypeDefBuilderHelper.createTypeDefinitionForTSymbol(errorType, errorTypeSymbol, @@ -9936,7 +9945,7 @@ private BRecordType createAnonRecordType(Location pos) { env.enclPkg.symbol.pkgID, null, null, pos, VIRTUAL); detailRecordTypeSymbol.scope = new Scope(detailRecordTypeSymbol); - BRecordType detailRecordType = new BRecordType(detailRecordTypeSymbol); + BRecordType detailRecordType = new BRecordType(symTable.typeEnv(), detailRecordTypeSymbol); detailRecordType.restFieldType = symTable.anydataType; return detailRecordType; } @@ -10094,7 +10103,7 @@ private void handleSafeNavigation(BLangBlockStmt blockStmt, BLangAccessExpressio if (!(accessExpr.errorSafeNavigation || accessExpr.nilSafeNavigation)) { BType originalType = Types.getImpliedType(accessExpr.originalType); if (isMapJson(originalType, false)) { - accessExpr.setBType(BUnionType.create(null, originalType, symTable.errorType)); + accessExpr.setBType(BUnionType.create(symTable.typeEnv(), null, originalType, symTable.errorType)); } else { accessExpr.setBType(originalType); } @@ -10269,7 +10278,7 @@ private BLangMatchClause getMatchAllAndNilReturnClause(BLangExpression matchExpr BLangVariableReference tempResultVarRef = ASTBuilderUtil.createVariableRef(pos, tempResultVar.symbol); BLangAssignment assignmentStmt = ASTBuilderUtil.createAssignmentStmt(pos, tempResultVarRef, createLiteral(pos, symTable.nilType, - Names.NIL_VALUE)); + Names.NIL_VALUE.value)); BLangBlockStmt clauseBody = ASTBuilderUtil.createBlockStmt(pos, this.env.scope, Lists.of(assignmentStmt)); BLangWildCardMatchPattern wildCardMatchPattern = ASTBuilderUtil.createWildCardMatchPattern(matchExpr); @@ -10322,7 +10331,8 @@ private BLangMatchClause getSuccessPatternClause(BType type, BLangExpression mat // and may not reflect the actual type of the child/field expr. if (TypeTags.isXMLTypeTag(Types.getImpliedType(tempAccessExpr.expr.getBType()).tag)) { // todo: add discription why this is special here - tempAccessExpr.setBType(BUnionType.create(null, accessExpr.originalType, symTable.errorType, + tempAccessExpr.setBType( + BUnionType.create(symTable.typeEnv(), null, accessExpr.originalType, symTable.errorType, symTable.nilType)); } else { tempAccessExpr.setBType(accessExpr.originalType); @@ -10436,8 +10446,8 @@ private BLangFunction createInitFunctionForClassDefn(BLangClassDefinition classD } BLangFunction initFunction = - TypeDefBuilderHelper.createInitFunctionForStructureType(classDefinition.symbol, env, names, - GENERATED_INIT_SUFFIX, classDefinition.getBType(), returnType); + TypeDefBuilderHelper.createInitFunctionForStructureType(symTable.typeEnv(), classDefinition.symbol, env, + names, GENERATED_INIT_SUFFIX, classDefinition.getBType(), returnType); BObjectTypeSymbol typeSymbol = ((BObjectTypeSymbol) classDefinition.getBType().tsymbol); typeSymbol.generatedInitializerFunc = new BAttachedFunction(GENERATED_INIT_SUFFIX, initFunction.symbol, (BInvokableType) initFunction.getBType(), null); @@ -10649,7 +10659,8 @@ public BLangLambdaFunction createArrowFunctionForNavigation(Location pos, List compUnitList = this.bLangPackage.getTestablePkg().getCompilationUnits(); - BArrayType bArrayType = new BArrayType(symTable.stringType, null, -1, BArrayState.OPEN); + BArrayType bArrayType = new BArrayType(symTable.typeEnv(), symTable.stringType, null, -1, BArrayState.OPEN); List modifiedcompUnitList = new ArrayList<>(); for (BLangCompilationUnit compUnit : compUnitList) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java index e114284e819d..d52d0000dd98 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/QueryDesugar.java @@ -17,6 +17,9 @@ package org.wso2.ballerinalang.compiler.desugar; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.clauses.OrderKeyNode; import org.ballerinalang.model.elements.Flag; @@ -299,7 +302,7 @@ BLangStatementExpression desugar(BLangQueryExpr queryExpr, SymbolEnv env, queryBlock, stmtsToBePropagated); BLangExpression result = streamRef; BLangLiteral isReadonly = ASTBuilderUtil.createLiteral(pos, symTable.booleanType, - Symbols.isFlagOn(queryExpr.getBType().flags, Flags.READONLY)); + Symbols.isFlagOn(queryExpr.getBType().getFlags(), Flags.READONLY)); BType resultType = queryExpr.getBType(); if (queryExpr.isStream) { resultType = streamRef.getBType(); @@ -324,14 +327,16 @@ BLangStatementExpression desugar(BLangQueryExpr queryExpr, SymbolEnv env, result = getStreamFunctionVariableRef(queryBlock, COLLECT_QUERY_FUNCTION, Lists.of(streamRef), pos); } else { BType refType = Types.getImpliedType(queryExpr.getBType()); - BType safeType = types.getSafeType(refType, true, true); - if (isXml(safeType)) { - if (types.isSubTypeOfReadOnly(refType, env)) { + SemType refSemType = refType.semType(); + SemType safeType = types.getNilAndErrorLiftType(refSemType); + if (SemTypes.isSubtypeSimpleNotNever(safeType, PredefinedType.XML)) { + if (types.isSubTypeOfReadOnly(refSemType)) { isReadonly.value = true; } result = getStreamFunctionVariableRef(queryBlock, QUERY_TO_XML_FUNCTION, Lists.of(streamRef, isReadonly), pos); - } else if (TypeTags.isStringTypeTag(safeType.tag)) { + } else if (PredefinedType.STRING.equals(safeType) || + SemTypes.isSameType(types.typeCtx(), safeType, PredefinedType.STRING_CHAR)) { result = getStreamFunctionVariableRef(queryBlock, QUERY_TO_STRING_FUNCTION, Lists.of(streamRef), pos); } else { BType arrayType = refType; @@ -608,11 +613,11 @@ BLangVariableReference addPipeline(BLangBlockStmt blockStmt, Location pos, constraintType = ((BStreamType) refType).constraint; completionType = ((BStreamType) refType).completionType; } - BType constraintTdType = new BTypedescType(constraintType, symTable.typeDesc.tsymbol); + BType constraintTdType = new BTypedescType(symTable.typeEnv(), constraintType, symTable.typeDesc.tsymbol); BLangTypedescExpr constraintTdExpr = new BLangTypedescExpr(); constraintTdExpr.resolvedType = constraintType; constraintTdExpr.setBType(constraintTdType); - BType completionTdType = new BTypedescType(completionType, symTable.typeDesc.tsymbol); + BType completionTdType = new BTypedescType(symTable.typeEnv(), completionType, symTable.typeDesc.tsymbol); BLangTypedescExpr completionTdExpr = new BLangTypedescExpr(); completionTdExpr.resolvedType = completionType; completionTdExpr.setBType(completionTdType); @@ -811,11 +816,11 @@ BLangVariableReference addOrderByFunction(BLangBlockStmt blockStmt, BLangOrderBy BLangArrayLiteral sortFieldsArrayExpr = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); sortFieldsArrayExpr.exprs = new ArrayList<>(); - sortFieldsArrayExpr.setBType(new BArrayType(symTable.anydataType)); + sortFieldsArrayExpr.setBType(new BArrayType(symTable.typeEnv(), symTable.anydataType)); BLangArrayLiteral sortModesArrayExpr = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); sortModesArrayExpr.exprs = new ArrayList<>(); - sortModesArrayExpr.setBType(new BArrayType(symTable.booleanType)); + sortModesArrayExpr.setBType(new BArrayType(symTable.typeEnv(), symTable.booleanType)); // Each order-key expression is added to sortFieldsArrayExpr. // Corresponding order-direction is added to sortModesArrayExpr. @@ -843,7 +848,7 @@ BLangVariableReference addGroupByFunction(BLangBlockStmt blockStmt, BLangGroupBy Location pos = groupByClause.pos; BLangArrayLiteral keys = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); keys.exprs = new ArrayList<>(); - keys.setBType(new BArrayType(symTable.stringType)); + keys.setBType(new BArrayType(symTable.typeEnv(), symTable.stringType)); for (BLangGroupingKey key :groupByClause.groupingKeyList) { if (key.variableDef == null) { keys.exprs.add(createStringLiteral(key.pos, key.variableRef.variableName.value)); @@ -858,7 +863,7 @@ BLangVariableReference addGroupByFunction(BLangBlockStmt blockStmt, BLangGroupBy BLangArrayLiteral nonGroupingKeys = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); nonGroupingKeys.exprs = new ArrayList<>(); - nonGroupingKeys.setBType(new BArrayType(symTable.stringType)); + nonGroupingKeys.setBType(new BArrayType(symTable.typeEnv(), symTable.stringType)); for (String nonGroupingKey : groupByClause.nonGroupingKeys) { nonGroupingKeys.exprs.add(createStringLiteral(pos, nonGroupingKey)); } @@ -871,7 +876,7 @@ BLangVariableReference addCollectFunction(BLangBlockStmt blockStmt, BLangCollect Location pos = collectClause.pos; BLangArrayLiteral nonGroupingKeys = (BLangArrayLiteral) TreeBuilder.createArrayLiteralExpressionNode(); nonGroupingKeys.exprs = new ArrayList<>(); - nonGroupingKeys.setBType(new BArrayType(symTable.stringType)); + nonGroupingKeys.setBType(new BArrayType(symTable.typeEnv(), symTable.stringType)); for (String nonGroupingKey : collectClause.nonGroupingKeys) { nonGroupingKeys.exprs.add(createStringLiteral(pos, nonGroupingKey)); } @@ -1444,7 +1449,7 @@ private BLangSimpleVarRef defineNilFrameForType(List symbols, BLangB private void addNilValueToFrame(BLangSimpleVarRef frameToAddValueTo, String key, BLangBlockStmt blockStmt, Location pos) { BLangStatement addToFrameStmt = getAddToFrameStmt(pos, frameToAddValueTo, key, - ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE)); + ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE.value)); blockStmt.addStatement(addToFrameStmt); } @@ -1549,7 +1554,8 @@ private BLangValueType getBooleanTypeNode() { */ private BLangUnionTypeNode getFrameErrorNilTypeNode() { BType frameType = getFrameTypeSymbol().type; - BUnionType unionType = BUnionType.create(null, frameType, symTable.errorType, symTable.nilType); + BUnionType unionType = + BUnionType.create(symTable.typeEnv(), null, frameType, symTable.errorType, symTable.nilType); BLangUnionTypeNode unionTypeNode = (BLangUnionTypeNode) TreeBuilder.createUnionTypeNode(); unionTypeNode.setBType(unionType); unionTypeNode.memberTypeNodes.add(getFrameTypeNode()); @@ -1560,7 +1566,7 @@ private BLangUnionTypeNode getFrameErrorNilTypeNode() { } private BLangUnionTypeNode getBooleanErrorTypeNode() { - BUnionType unionType = BUnionType.create(null, symTable.errorType, symTable.booleanType); + BUnionType unionType = BUnionType.create(symTable.typeEnv(), null, symTable.errorType, symTable.booleanType); BLangUnionTypeNode unionTypeNode = (BLangUnionTypeNode) TreeBuilder.createUnionTypeNode(); unionTypeNode.setBType(unionType); unionTypeNode.memberTypeNodes.add(getErrorTypeNode()); @@ -1570,7 +1576,7 @@ private BLangUnionTypeNode getBooleanErrorTypeNode() { } private BLangUnionTypeNode getIntErrorTypeNode() { - BUnionType unionType = BUnionType.create(null, symTable.errorType, symTable.intType); + BUnionType unionType = BUnionType.create(symTable.typeEnv(), null, symTable.errorType, symTable.intType); BLangUnionTypeNode unionTypeNode = (BLangUnionTypeNode) TreeBuilder.createUnionTypeNode(); unionTypeNode.setBType(unionType); unionTypeNode.memberTypeNodes.add(getErrorTypeNode()); @@ -1585,7 +1591,7 @@ private BLangUnionTypeNode getIntErrorTypeNode() { * @return a any & error type node. */ private BLangUnionTypeNode getAnyAndErrorTypeNode() { - BUnionType unionType = BUnionType.create(null, symTable.anyType, symTable.errorType); + BUnionType unionType = BUnionType.create(symTable.typeEnv(), null, symTable.anyType, symTable.errorType); BLangUnionTypeNode unionTypeNode = (BLangUnionTypeNode) TreeBuilder.createUnionTypeNode(); unionTypeNode.memberTypeNodes.add(getAnyTypeNode()); unionTypeNode.memberTypeNodes.add(getErrorTypeNode()); @@ -1738,7 +1744,7 @@ public void visit(BLangCollectContextInvocation collectContextInvocation) { if (isNilReturnInvocationInCollectClause(invocation)) { Location pos = invocation.pos; BLangSimpleVarRef restArg = (BLangSimpleVarRef) invocation.argExprs.get(0); - BType invocationType = BUnionType.create(null, invocation.getBType(), symTable.nilType); + BType invocationType = BUnionType.create(symTable.typeEnv(), null, invocation.getBType(), symTable.nilType); BLangSimpleVariable tempResultVar = ASTBuilderUtil.createVariable(pos, "$invocationResult$", invocationType, null, new BVarSymbol(0, Names.fromString("$invocationResult$"), this.env.scope.owner.pkgID, invocationType, this.env.scope.owner, pos, VIRTUAL)); @@ -1812,7 +1818,7 @@ private BType changeSeqSymbolType(BSymbol symbol) { BType elementType = ((BSequenceType) symbol.type).elementType; List tupleMembers = new ArrayList<>(1); tupleMembers.add(new BTupleMember(elementType, Symbols.createVarSymbolForTupleMember(elementType))); - symbol.type = new BTupleType(null, tupleMembers, elementType, 0); + symbol.type = new BTupleType(symTable.typeEnv(), null, tupleMembers, elementType, 0); } return symbol.type; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ServiceDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ServiceDesugar.java index da39e5efca41..7f2c356e2c2c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ServiceDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/ServiceDesugar.java @@ -241,7 +241,7 @@ private BType getListenerTypeWithoutError(BType type) { } members.add(memberType); } - return BUnionType.create(null, members); + return BUnionType.create(symTable.typeEnv(), null, members); } return type; } @@ -268,7 +268,7 @@ private void addMethodInvocation(Location pos, BLangSimpleVarRef varRef, BInvoka // call is generated in BIRGen. Casting to the first listener type should be fine as actual method invocation // is based on the value rather than the type. BType listenerType = getListenerType(varRef.getBType()); - if (!types.isSameType(listenerType, varRef.getBType())) { + if (!types.isSameTypeIncludingTags(listenerType, varRef.getBType())) { BLangTypeConversionExpr castExpr = (BLangTypeConversionExpr) TreeBuilder.createTypeConversionNode(); castExpr.expr = varRef; castExpr.setBType(listenerType); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java index d81adba0d815..4c3137efc1f3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/desugar/TransactionDesugar.java @@ -205,7 +205,7 @@ private BLangBlockStmt desugarTransactionBody(BLangTransaction transactionNode, BType transactionReturnType = symTable.errorOrNilType; // wraps content within transaction body inside a statement expression - BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE); + BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE.value); BLangStatementExpression statementExpression = createStatementExpression(transactionNode.transactionBody, nilLiteral); statementExpression.setBType(symTable.nilType); @@ -373,7 +373,7 @@ public void startTransactionCoordinatorOnce(SymbolEnv env, Location pos) { } BLangSimpleVariableDef createPrevAttemptInfoVarDef(SymbolEnv env, Location pos) { - BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE); + BLangLiteral nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE.value); BLangSimpleVariable prevAttemptVariable = createPrevAttemptVariable(env, pos); prevAttemptVariable.expr = nilLiteral; return ASTBuilderUtil.createVariableDef(pos, prevAttemptVariable); @@ -384,7 +384,7 @@ private BLangSimpleVariable createPrevAttemptVariable(SymbolEnv env, Location po BSymbol infoRecordSymbol = symResolver. lookupSymbolInMainSpace(symTable.pkgEnvMap.get(symTable.langTransactionModuleSymbol), TRANSACTION_INFO_RECORD); - BType infoRecordType = BUnionType.create(null, infoRecordSymbol.type, symTable.nilType); + BType infoRecordType = BUnionType.create(symTable.typeEnv(), null, infoRecordSymbol.type, symTable.nilType); BVarSymbol prevAttemptVarSymbol = new BVarSymbol(0, new Name("prevAttempt" + uniqueId), env.scope.owner.pkgID, infoRecordType, env.scope.owner, pos, VIRTUAL); prevAttemptVarSymbol.closure = true; @@ -515,7 +515,7 @@ BLangStatementExpression invokeRollbackFunc(Location pos, BLangExpression rollba BLangExpressionStmt cleanUpTrx = ASTBuilderUtil.createExpressionStmt(pos, rollbackBlockStmt); cleanUpTrx.expr = createCleanupTrxStmt(pos, trxBlockId); BLangStatementExpression rollbackStmtExpr = createStatementExpression(rollbackBlockStmt, - ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE)); + ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE.value)); rollbackStmtExpr.setBType(symTable.nilType); //at this point, @@ -564,7 +564,7 @@ BLangStatementExpression desugar(BLangCommitExpr commitExpr, SymbolEnv env) { createInvocationExprForMethod(pos, commitTransactionInvokableSymbol, args, symResolver); commitTransactionInvocation.argExprs = args; - BType commitReturnType = BUnionType.create(null, symTable.stringType, symTable.errorType); + BType commitReturnType = BUnionType.create(symTable.typeEnv(), null, symTable.stringType, symTable.errorType); BVarSymbol commitTransactionVarSymbol = new BVarSymbol(0, new Name("commitResult"), env.scope.owner.pkgID, commitReturnType, env.scope.owner, pos, VIRTUAL); @@ -611,8 +611,7 @@ BLangStatementExpression desugar(BLangCommitExpr commitExpr, SymbolEnv env) { isTransactionFailedVariable.symbol); List paramTypes = new ArrayList<>(); paramTypes.add(symTable.booleanType); - BInvokableType type = new BInvokableType(paramTypes, symTable.booleanType, - null); + BInvokableType type = new BInvokableType(symTable.typeEnv(), paramTypes, symTable.booleanType, null); BOperatorSymbol notOperatorSymbol = new BOperatorSymbol( Names.fromString(OperatorKind.NOT.value()), symTable.rootPkgSymbol.pkgID, type, symTable.rootPkgSymbol, symTable.builtinPos, VIRTUAL); @@ -640,7 +639,7 @@ BLangStatementExpression desugar(BLangCommitExpr commitExpr, SymbolEnv env) { } private BLangSimpleVariableDef createCommitResultVarDef(SymbolEnv env, Location pos) { - BLangExpression nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE); + BLangExpression nilLiteral = ASTBuilderUtil.createLiteral(pos, symTable.nilType, Names.NIL_VALUE.value); BVarSymbol outputVarSymbol = new BVarSymbol(0, new Name("$outputVar$"), env.scope.owner.pkgID, symTable.errorOrNilType, env.scope.owner, pos, VIRTUAL); BLangSimpleVariable outputVariable = diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java index aa698df5108e..83d503eb31ab 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/BLangNodeBuilder.java @@ -911,7 +911,6 @@ private void createAnonymousTypeDefForConstantDeclaration(BLangConstant constant BLangUnaryExpr unaryExpr = createBLangUnaryExpr(unaryConstant.pos, unaryConstant.operator, unaryConstant.expr); unaryExpr.setBType(unaryConstant.expr.getBType()); - unaryExpr.isConstant = true; finiteTypeNode.valueSpace.add(unaryExpr); } finiteTypeNode.pos = identifierPos; @@ -2794,7 +2793,7 @@ public BLangNode transform(ReturnStatementNode returnStmtNode) { } else { BLangLiteral nilLiteral = (BLangLiteral) TreeBuilder.createLiteralExpression(); nilLiteral.pos = getPosition(returnStmtNode); - nilLiteral.value = Names.NIL_VALUE; + nilLiteral.value = Names.NIL_VALUE.value; nilLiteral.setBType(symTable.nilType); bLReturn.expr = nilLiteral; } @@ -6115,6 +6114,7 @@ private BLangLiteral createSimpleLiteral(Node literal, boolean isFiniteType) { typeTag = TypeTags.INT; value = getIntegerLiteral(literal, textValue); originalValue = textValue; + // TODO: can we fix below? if (literalTokenKind == SyntaxKind.HEX_INTEGER_LITERAL_TOKEN && withinByteRange(value)) { typeTag = TypeTags.BYTE; } @@ -6184,9 +6184,9 @@ private BLangLiteral createSimpleLiteral(Node literal, boolean isFiniteType) { originalValue = textValue; bLiteral = (BLangLiteral) TreeBuilder.createLiteralExpression(); } else if (type == SyntaxKind.NIL_LITERAL) { - originalValue = "()"; + originalValue = Names.NIL_VALUE.value; typeTag = TypeTags.NIL; - value = "()"; + value = Names.NIL_VALUE.value; bLiteral = (BLangLiteral) TreeBuilder.createLiteralExpression(); } else if (type == SyntaxKind.NULL_LITERAL) { originalValue = "null"; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java index 75db8cf2452f..cccd4bdf22b0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/parser/NodeCloner.java @@ -399,6 +399,7 @@ private void cloneBLangType(BLangType source, BLangType clone) { clone.nullable = source.nullable; clone.grouped = source.grouped; clone.flagSet = cloneSet(source.flagSet, Flag.class); + clone.defn = source.defn; } private > EnumSet cloneSet(Set source, Class elementType) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java index a3920e3d2720..9c5229a36704 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java @@ -19,6 +19,10 @@ import io.ballerina.identifier.Utils; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.Value; import org.ballerinalang.compiler.CompilerPhase; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -267,6 +271,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; @@ -1086,16 +1091,8 @@ private HashMap getConstValue(BLangConstPattern constPattern) { } private Object getConstValueFromFiniteType(BFiniteType type) { - if (type.getValueSpace().size() == 1) { - BLangExpression expr = type.getValueSpace().iterator().next(); - switch (expr.getKind()) { - case NUMERIC_LITERAL: - return ((BLangNumericLiteral) expr).value; - case LITERAL: - return ((BLangLiteral) expr).value; - } - } - return null; + Optional value = Core.singleShape(type.semType()); + return value.map(v -> v.value).orElse(null); } private boolean checkSimilarListMatchPattern(BLangListMatchPattern firstListMatchPattern, @@ -1402,10 +1399,8 @@ public void visit(BLangVarBindingPatternMatchPattern varBindingPattern, Analyzer if (varBindingPattern.matchExpr == null) { return; } - varBindingPattern.isLastPattern = types.isSameType(varBindingPattern.matchExpr.getBType(), - varBindingPattern.getBType()) || types.isAssignable( - varBindingPattern.matchExpr.getBType(), - varBindingPattern.getBType()); + varBindingPattern.isLastPattern = types.isAssignable(varBindingPattern.matchExpr.getBType(), + varBindingPattern.getBType()); } } @@ -1534,7 +1529,8 @@ public void visit(BLangFail failNode, AnalyzerData data) { if (!data.failureHandled) { BType exprType = data.env.enclInvokable.getReturnTypeNode().getBType(); data.returnTypes.peek().add(exprType); - if (!types.isAssignable(types.getErrorTypes(failNode.expr.getBType()), exprType)) { + BType type = failNode.expr.getBType(); + if (!types.isSubtype(types.getErrorIntersection(type.semType()), exprType.semType())) { dlog.error(failNode.pos, DiagnosticErrorCode.FAIL_EXPR_NO_MATCHING_ERROR_RETURN_IN_ENCL_INVOKABLE); } } @@ -1653,7 +1649,7 @@ private void checkForExportableType(BTypeSymbol symbol, Location pos, HashSet 1) { - sendTypeWithNoMsgIgnored = BUnionType.create(null, returnTypeAndSendType); + sendTypeWithNoMsgIgnored = BUnionType.create(symTable.typeEnv(), null, returnTypeAndSendType); } else { sendTypeWithNoMsgIgnored = exprType; } @@ -2098,7 +2094,7 @@ private void setWorkerSendSendTypeDetails(BLangWorkerSendExpr workerSendExpr, BT lookup(Names.fromString(NO_MESSAGE_ERROR_TYPE)).symbol; returnTypeAndSendType.add(noMsgErrSymbol.getType()); if (returnTypeAndSendType.size() > 1) { - sendType = BUnionType.create(null, returnTypeAndSendType); + sendType = BUnionType.create(symTable.typeEnv(), null, returnTypeAndSendType); } else { sendType = exprType; } @@ -2143,7 +2139,7 @@ public void visit(BLangWorkerSyncSendExpr syncSendExpr, AnalyzerData data) { was.hasErrors = true; } - syncSendExpr.setBType(BUnionType.create(null, symTable.nilType, symTable.errorType)); + syncSendExpr.setBType(BUnionType.create(symTable.typeEnv(), null, symTable.nilType, symTable.errorType)); boolean withinIfOrOnFail = !invalidSendPos && withinIfOrOnFail(data.env.enclInvokable.body, data.env.node); setWorkerSendSendTypeDetails(syncSendExpr, syncSendExpr.expr.getBType(), withinIfOrOnFail, data); was.addWorkerAction(syncSendExpr); @@ -2254,7 +2250,7 @@ public BType createAccumulatedErrorTypeForMatchingSyncSend(AnalyzerData data, Lo returnTypeAndSendType.add(symTable.nilType); if (returnTypeAndSendType.size() > 1) { - return BUnionType.create(null, returnTypeAndSendType); + return BUnionType.create(symTable.typeEnv(), null, returnTypeAndSendType); } else { return symTable.nilType; } @@ -2279,25 +2275,7 @@ private void addErrorTypesToSet(BType returnType, LinkedHashSet errorType } private boolean hasNonErrorType(BType returnType) { - if (returnType == null) { - return false; - } - - BType effType = Types.getImpliedType(types.getTypeWithEffectiveIntersectionTypes(returnType)); - if (effType.tag == TypeTags.ERROR) { - return false; - } - - if (effType.tag == TypeTags.UNION) { - for (BType memberType : ((BUnionType) returnType).getMemberTypes()) { - if (hasNonErrorType(memberType)) { - return true; - } - } - return false; - } - - return true; + return !Core.isSubtypeSimple(returnType.semType(), PredefinedType.ERROR); } @Override @@ -3340,14 +3318,14 @@ public void visit(BLangCheckedExpr checkedExpr, AnalyzerData data) { BType exprType = Types.getImpliedType(enclInvokable.getReturnTypeNode().getBType()); BType checkedExprType = checkedExpr.expr.getBType(); - BType errorType = types.getErrorTypes(checkedExprType); + SemType errorType = types.getErrorIntersection(checkedExprType.semType()); - if (errorType == symTable.semanticError) { + if (Core.isNever(errorType)) { return; } boolean ignoreErrForCheckExpr = data.withinQuery && data.queryConstructType == Types.QueryConstructType.STREAM; - if (!data.failureHandled && !ignoreErrForCheckExpr && !types.isAssignable(errorType, exprType) + if (!data.failureHandled && !ignoreErrForCheckExpr && !types.isSubtype(errorType, exprType.semType()) && !types.isNeverTypeOrStructureTypeWithARequiredNeverMember(checkedExprType)) { dlog.error(checkedExpr.pos, DiagnosticErrorCode.CHECKED_EXPR_NO_MATCHING_ERROR_RETURN_IN_ENCL_INVOKABLE); @@ -3546,7 +3524,7 @@ public void visit(BLangTypeTestExpr typeTestExpr, AnalyzerData data) { // It'll be only possible iff, the target type has been assigned to the source // variable at some point. To do that, a value of target type should be assignable // to the type of the source variable. - if (!intersectionExists(expr, typeNodeType, data, typeTestExpr.pos)) { + if (!types.intersectionExists(expr.getBType().semType(), typeNodeType.semType())) { dlog.error(typeTestExpr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPE_CHECK, exprType, typeNodeType); } } @@ -3575,20 +3553,6 @@ private void logDeprecatedWaring(String deprecatedConstruct, BSymbol symbol, Loc dlog.warning(pos, DiagnosticWarningCode.USAGE_OF_DEPRECATED_CONSTRUCT, deprecatedConstruct); } - private boolean intersectionExists(BLangExpression expression, BType testType, AnalyzerData data, - Location intersectionPos) { - BType expressionType = expression.getBType(); - - BType intersectionType = types.getTypeIntersection( - Types.IntersectionContext.typeTestIntersectionExistenceContext(intersectionPos), - expressionType, testType, data.env); - - // any and readonly has an intersection - return (intersectionType != symTable.semanticError) || - (Types.getImpliedType(expressionType).tag == TypeTags.ANY && - Types.getImpliedType(testType).tag == TypeTags.READONLY); - } - @Override public void visit(BLangInferredTypedescDefaultNode inferTypedescExpr, AnalyzerData data) { /* Ignore */ @@ -3817,7 +3781,7 @@ private void typeCheckAlternateReceive(BLangAlternateWorkerReceive altWorkerRv) BType actualType; if (altTypes.size() > 1) { - actualType = BUnionType.create(null, altTypes); + actualType = BUnionType.create(symTable.typeEnv(), null, altTypes); } else { actualType = altTypes.iterator().next(); } @@ -4295,7 +4259,7 @@ private void validateInvocationInMatchGuard(BLangInvocation invocation) { BType matchedExprType = matchedExpr.getBType(); if (types.isInherentlyImmutableType(matchedExprType) || - Symbols.isFlagOn(matchedExprType.flags, Flags.READONLY)) { + Symbols.isFlagOn(matchedExprType.getFlags(), Flags.READONLY)) { return; } @@ -4319,7 +4283,7 @@ private void validateInvocationInMatchGuard(BLangInvocation invocation) { BLangExpression streamImplementorExpr = argsExpr.get(0); BType type = streamImplementorExpr.getBType(); - if (!types.isInherentlyImmutableType(type) && !Symbols.isFlagOn(type.flags, Flags.READONLY)) { + if (!types.isInherentlyImmutableType(type) && !Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { dlog.error(streamImplementorExpr.pos, DiagnosticErrorCode.INVALID_CALL_WITH_MUTABLE_ARGS_IN_MATCH_GUARD); } @@ -4344,7 +4308,7 @@ private void validateInvocationInMatchGuard(BLangInvocation invocation) { if (type != symTable.semanticError && !types.isInherentlyImmutableType(type) && - !Symbols.isFlagOn(type.flags, Flags.READONLY)) { + !Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { dlog.error(arg.pos, DiagnosticErrorCode.INVALID_CALL_WITH_MUTABLE_ARGS_IN_MATCH_GUARD); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConditionResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConditionResolver.java index 7b667249be30..a2fddc62af1f 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConditionResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConditionResolver.java @@ -17,6 +17,8 @@ */ package org.wso2.ballerinalang.compiler.semantics.analyzer; +import io.ballerina.types.Core; +import io.ballerina.types.Value; import org.ballerinalang.model.tree.OperatorKind; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.SymTag; @@ -29,8 +31,9 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef; import org.wso2.ballerinalang.compiler.tree.expressions.BLangTypeTestExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangUnaryExpr; +import org.wso2.ballerinalang.compiler.util.TypeTags; -import java.util.HashSet; +import java.util.Optional; /** * This analyzes the condition in if statement and while statement. @@ -51,9 +54,11 @@ static BType checkConstCondition(Types types, SymbolTable symTable, BLangExpress if (value instanceof Boolean) { return value == Boolean.TRUE ? symTable.trueType : symTable.falseType; } - return new BFiniteType(null, new HashSet<>() { { add(condition); } }); + return BFiniteType.newSingletonBFiniteType(null, + SemTypeHelper.resolveSingletonType((BLangLiteral) condition)); case NUMERIC_LITERAL: - return new BFiniteType(null, new HashSet<>() { { add(condition); } }); + return BFiniteType.newSingletonBFiniteType(null, + SemTypeHelper.resolveSingletonType((BLangLiteral) condition)); case TYPE_TEST_EXPR: BLangTypeTestExpr typeTestExpr = (BLangTypeTestExpr) condition; BType exprType = typeTestExpr.expr.getBType(); @@ -119,11 +124,21 @@ static BType checkConstCondition(Types types, SymbolTable symTable, BLangExpress BLangSimpleVarRef simpleVarRef = (BLangSimpleVarRef) condition; BType type = (simpleVarRef.symbol.tag & SymTag.CONSTANT) == SymTag.CONSTANT ? simpleVarRef.symbol.type : condition.getBType(); - if (!types.isSingletonType(type)) { + BType baseType = Types.getImpliedType(type); + + if (baseType.tag != TypeTags.FINITE) { + return symTable.semanticError; + } + + Optional val = Core.singleShape(baseType.semType()); + if (val.isEmpty()) { return symTable.semanticError; } - return checkConstCondition(types, symTable, ((BFiniteType) Types.getImpliedType(type)) - .getValueSpace().iterator().next()); + + if (val.get().value instanceof Boolean) { + return val.get().value == Boolean.TRUE ? symTable.trueType : symTable.falseType; + } + return baseType; default: return symTable.semanticError; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java index 19b37c0e15c3..07a643dfa4b1 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantTypeChecker.java @@ -19,6 +19,12 @@ import io.ballerina.tools.diagnostics.DiagnosticCode; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.Value; +import io.ballerina.types.subtypedata.StringSubtype; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -47,7 +53,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -58,7 +63,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -111,11 +115,13 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.function.BiFunction; import static org.ballerinalang.model.symbols.SymbolOrigin.SOURCE; import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL; +import static org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper.singleShapeBroadType; /** * Resolve the value and check the type of constant expression. @@ -148,7 +154,7 @@ public ConstantTypeChecker(CompilerContext context) { this.types = Types.getInstance(context); this.anonymousModelHelper = BLangAnonymousModelHelper.getInstance(context); this.typeChecker = TypeChecker.getInstance(context); - this.typeResolver = TypeResolver.getInstance(context); + this.typeResolver = TypeResolver.getInstance(context);; this.fillMembers = FillMembers.getInstance(context); } @@ -261,7 +267,7 @@ public void visit(BLangLiteral literalExpr, AnalyzerData data) { return; } - BType finiteType = getFiniteType(literalExpr.value, data.constantSymbol, literalExpr.pos, literalType); + BType finiteType = getFiniteType(literalExpr.value, data.constantSymbol, literalType); if (data.compoundExprCount == 0 && types.typeIncompatible(literalExpr.pos, finiteType, data.expType)) { data.resultType = symTable.semanticError; @@ -275,14 +281,14 @@ private BType rewriteByteArrayLiteral(BLangLiteral literalExpr, AnalyzerData dat List memberTypes = new ArrayList<>(); for (byte b : values) { - memberTypes.add(getFiniteType(Byte.toUnsignedLong(b), data.constantSymbol, literalExpr.pos, - symTable.intType)); + memberTypes.add(getFiniteType(Byte.toUnsignedLong(b), data.constantSymbol, symTable.intType)); } BType expType = Types.getImpliedType(data.expType); if (expType.tag == TypeTags.ARRAY && ((BArrayType) expType).state == BArrayState.INFERRED) { - ((BArrayType) expType).size = memberTypes.size(); - ((BArrayType) expType).state = BArrayState.CLOSED; + BArrayType expArrayType = (BArrayType) expType; + expArrayType.setSize(memberTypes.size()); + expArrayType.state = BArrayState.CLOSED; } return createNewTupleType(literalExpr.pos, memberTypes, data); @@ -459,7 +465,7 @@ public void visit(BLangBinaryExpr binaryExpr, AnalyzerData data) { data.resultType = symTable.semanticError; return; } - BType finiteType = getFiniteType(resolvedValue, constantSymbol, pos, resultType); + BType finiteType = getFiniteType(resolvedValue, constantSymbol, resultType); if (data.compoundExprCount == 0 && types.typeIncompatible(pos, finiteType, expType)) { data.resultType = symTable.semanticError; return; @@ -496,14 +502,13 @@ public void visit(BLangUnaryExpr unaryExpr, AnalyzerData data) { } BConstantSymbol constantSymbol = data.constantSymbol; - Object resolvedValue = evaluateUnaryOperator((BFiniteType) actualType, resultType, - unaryExpr.operator, data); + Object resolvedValue = evaluateUnaryOperator((BFiniteType) actualType, resultType, unaryExpr.operator, data); if (resolvedValue == null) { data.resultType = symTable.semanticError; return; } - BType finiteType = getFiniteType(resolvedValue, constantSymbol, unaryExpr.pos, resultType); + BType finiteType = getFiniteType(resolvedValue, constantSymbol, resultType); if (data.compoundExprCount == 0 && types.typeIncompatible(unaryExpr.pos, finiteType, data.expType)) { data.resultType = symTable.semanticError; return; @@ -513,7 +518,7 @@ public void visit(BLangUnaryExpr unaryExpr, AnalyzerData data) { private BRecordType createNewRecordType(BRecordTypeSymbol symbol, LinkedHashMap inferredFields, AnalyzerData data) { - BRecordType recordType = new BRecordType(symbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), symbol); recordType.restFieldType = symTable.noType; recordType.fields = inferredFields; symbol.type = recordType; @@ -537,8 +542,8 @@ private BType checkMappingConstructorCompatibility(BType expType, BLangRecordLit } if (tag == TypeTags.INTERSECTION) { - return checkMappingConstructorCompatibility(((BIntersectionType) expType).effectiveType, - mappingConstructor, data); + return checkMappingConstructorCompatibility(((BIntersectionType) expType).effectiveType, mappingConstructor, + data); } BType possibleType = getMappingConstructorCompatibleNonUnionType(expType, data); @@ -614,7 +619,7 @@ private BType checkMappingConstructorCompatibilityForUnionType(BType expType, BL mappingConstructor, data); if (memCompatibiltyType != symTable.semanticError && dlog.errorCount() == 0 && - isUniqueType(compatibleTypes, listCompatibleMemType)) { + types.isUniqueType(compatibleTypes, listCompatibleMemType)) { compatibleTypes.add(listCompatibleMemType); } } @@ -642,15 +647,15 @@ private BType getMappingConstructorCompatibleNonUnionType(BType type, AnalyzerDa case TypeTags.READONLY: return type; case TypeTags.JSON: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapJsonType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapJsonType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapJsonType, data.env, symTable, anonymousModelHelper, names); case TypeTags.ANYDATA: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapAnydataType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapAnydataType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapAnydataType, data.env, symTable, anonymousModelHelper, names); case TypeTags.ANY: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapAllType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapAllType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapAllType, data.env, symTable, anonymousModelHelper, names); case TypeTags.INTERSECTION: @@ -758,20 +763,22 @@ private BType validateMapTypeAndInferredType(BLangRecordLiteral mappingConstruct BLangRecordLiteral.BLangRecordKeyValueField keyValue = (BLangRecordLiteral.BLangRecordKeyValueField) field; BLangRecordLiteral.BLangRecordKey key = keyValue.key; BType fieldName = checkConstExpr(key.expr, data); - if (fieldName.tag == TypeTags.FINITE && ((BFiniteType) fieldName).getValueSpace().size() == 1) { - BLangLiteral fieldNameLiteral = - (BLangLiteral) ((BFiniteType) fieldName).getValueSpace().iterator().next(); - if (fieldNameLiteral.getBType().tag == TypeTags.STRING) { - exprToCheck = keyValue.valueExpr; - if (data.commonAnalyzerData.nonErrorLoggingCheck) { - exprToCheck = nodeCloner.cloneNode(keyValue.valueExpr); - } - BType keyValueType = checkConstExpr(exprToCheck, expType, data); - if (!addFields(inferredFields, Types.getImpliedType(keyValueType), - fieldNameLiteral.getValue().toString(), key.pos, recordSymbol)) { - containErrors = true; + if (fieldName.tag == TypeTags.FINITE) { + SemType semtype = fieldName.semType(); + if (SemTypes.isSubtypeSimple(semtype, PredefinedType.STRING)) { + Optional str = StringSubtype.stringSubtypeSingleValue(Core.stringSubtype(semtype)); + if (str.isPresent()) { + exprToCheck = keyValue.valueExpr; + if (data.commonAnalyzerData.nonErrorLoggingCheck) { + exprToCheck = nodeCloner.cloneNode(keyValue.valueExpr); + } + BType keyValueType = checkConstExpr(exprToCheck, expType, data); + if (!addFields(inferredFields, Types.getImpliedType(keyValueType), str.get(), key.pos, + recordSymbol)) { + containErrors = true; + } + continue; } - continue; } } dlog.error(key.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, symTable.stringType, fieldName); @@ -898,33 +905,35 @@ private BType validateRecordType(BLangRecordLiteral mappingConstructor, BRecordT BLangRecordLiteral.BLangRecordKeyValueField keyValue = (BLangRecordLiteral.BLangRecordKeyValueField) field; BLangRecordLiteral.BLangRecordKey key = keyValue.key; BType fieldName = checkConstExpr(key.expr, data); - if (fieldName.tag == TypeTags.FINITE && ((BFiniteType) fieldName).getValueSpace().size() == 1) { - BLangLiteral fieldNameLiteral = - (BLangLiteral) ((BFiniteType) fieldName).getValueSpace().iterator().next(); - if (fieldNameLiteral.getBType().tag == TypeTags.STRING) { - String keyName = fieldNameLiteral.getValue().toString(); - if (!targetFields.containsKey(keyName)) { - if (expRecordType.sealed) { - containErrors = true; - dlog.error(keyValue.pos, DiagnosticErrorCode.UNDEFINED_STRUCTURE_FIELD_WITH_TYPE, - key, expRecordType.tsymbol.type.getKind().typeName(), expRecordType); - continue; + if (fieldName.tag == TypeTags.FINITE) { + SemType semtype = fieldName.semType(); + if (SemTypes.isSubtypeSimple(semtype, PredefinedType.STRING)) { + Optional str = StringSubtype.stringSubtypeSingleValue(Core.stringSubtype(semtype)); + if (str.isPresent()) { + String keyName = str.get(); + if (!targetFields.containsKey(keyName)) { + if (expRecordType.sealed) { + containErrors = true; + dlog.error(keyValue.pos, DiagnosticErrorCode.UNDEFINED_STRUCTURE_FIELD_WITH_TYPE, + key, expRecordType.tsymbol.type.getKind().typeName(), expRecordType); + continue; + } else { + expType = expRecordType.restFieldType; + } } else { - expType = expRecordType.restFieldType; + expType = targetFields.get(keyName).type; } - } else { - expType = targetFields.get(keyName).type; - } - exprToCheck = keyValue.valueExpr; - if (data.commonAnalyzerData.nonErrorLoggingCheck) { - exprToCheck = nodeCloner.cloneNode(keyValue.valueExpr); - } - BType keyValueType = checkConstExpr(exprToCheck, expType, data); - if (!addFields(inferredFields, Types.getImpliedType(keyValueType), - keyName, key.pos, recordSymbol)) { - containErrors = true; + exprToCheck = keyValue.valueExpr; + if (data.commonAnalyzerData.nonErrorLoggingCheck) { + exprToCheck = nodeCloner.cloneNode(keyValue.valueExpr); + } + BType keyValueType = checkConstExpr(exprToCheck, expType, data); + if (!addFields(inferredFields, Types.getImpliedType(keyValueType), + keyName, key.pos, recordSymbol)) { + containErrors = true; + } + continue; } - continue; } } dlog.error(key.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, fieldName, symTable.stringType); @@ -938,22 +947,6 @@ private BType validateRecordType(BLangRecordLiteral mappingConstructor, BRecordT return createNewRecordType(recordSymbol, inferredFields, data); } - private boolean isUniqueType(Iterable typeList, BType type) { - boolean isRecord = type.tag == TypeTags.RECORD; - - for (BType bType : typeList) { - - if (isRecord) { - if (type == bType) { - return false; - } - } else if (types.isSameType(type, bType)) { - return false; - } - } - return true; - } - private boolean validateRequiredFields(BRecordType type, List specifiedFields, Location pos, AnalyzerData data) { HashSet specFieldNames = getFieldNames(specifiedFields, data); @@ -1073,7 +1066,7 @@ private BType checkListConstructorCompatibility(BType expType, BLangListConstruc BType memCompatibiltyType = checkListConstructorCompatibility(listCompatibleMemType, listConstructor, data); if (memCompatibiltyType != symTable.semanticError && dlog.errorCount() == 0 && - isUniqueType(compatibleTypes, memCompatibiltyType)) { + types.isUniqueType(compatibleTypes, memCompatibiltyType)) { compatibleTypes.add(memCompatibiltyType); } } @@ -1101,8 +1094,8 @@ private BType checkListConstructorCompatibility(BType expType, BLangListConstruc } if (tag == TypeTags.INTERSECTION) { - return checkListConstructorCompatibility(((BIntersectionType) expType).effectiveType, - listConstructor, data); + return checkListConstructorCompatibility(((BIntersectionType) expType).effectiveType, listConstructor, + data); } BType possibleType = getListConstructorCompatibleNonUnionType(expType, data); @@ -1134,7 +1127,7 @@ private BType checkArrayType(BArrayType arrayType, BLangListConstructorExpr list switch (spreadOpType.tag) { case TypeTags.ARRAY: - int arraySize = ((BArrayType) spreadOpType).size; + int arraySize = ((BArrayType) spreadOpType).getSize(); if (arraySize >= 0) { listExprSize += arraySize; continue; @@ -1161,12 +1154,12 @@ private BType checkArrayType(BArrayType arrayType, BLangListConstructorExpr list BType eType = arrayType.eType; if (arrayType.state == BArrayState.INFERRED) { - arrayType.size = listExprSize; + arrayType.setSize(listExprSize); arrayType.state = BArrayState.CLOSED; - } else if (arrayType.state != BArrayState.OPEN && arrayType.size != listExprSize) { - if (arrayType.size < listExprSize) { - dlog.error(listConstructor.pos, DiagnosticErrorCode.MISMATCHING_ARRAY_LITERAL_VALUES, arrayType.size, - listExprSize); + } else if (arrayType.state != BArrayState.OPEN && arrayType.getSize() != listExprSize) { + if (arrayType.getSize() < listExprSize) { + dlog.error(listConstructor.pos, DiagnosticErrorCode.MISMATCHING_ARRAY_LITERAL_VALUES, + arrayType.getSize(), listExprSize); return symTable.semanticError; } @@ -1217,7 +1210,7 @@ private BType checkArrayType(BArrayType arrayType, BLangListConstructorExpr list // Create new tuple type using inferred members. BTupleType resultTupleType = createNewTupleType(listConstructor.pos, memberTypes, data); - if (arrayType.state == BArrayState.CLOSED && arrayType.size > listExprSize) { + if (arrayType.state == BArrayState.CLOSED && arrayType.getSize() > listExprSize) { if (!fillMembers.addFillMembers(resultTupleType, arrayType, data)) { return symTable.semanticError; } @@ -1245,7 +1238,7 @@ private BType checkTupleType(BTupleType tupleType, BLangListConstructorExpr list switch (spreadOpType.tag) { case TypeTags.ARRAY: - int arraySize = ((BArrayType) spreadOpType).size; + int arraySize = ((BArrayType) spreadOpType).getSize(); if (arraySize >= 0) { listExprSize += arraySize; continue; @@ -1359,7 +1352,7 @@ private BTupleType createNewTupleType(Location pos, List memberTypes, Ana List members = new ArrayList<>(); memberTypes.forEach(m -> members.add(new BTupleMember(m, Symbols.createVarSymbolForTupleMember(m)))); - BTupleType tupleType = new BTupleType(tupleTypeSymbol, members); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, members); tupleType.tsymbol.type = tupleType; return tupleType; } @@ -1386,13 +1379,13 @@ private BType getListConstructorCompatibleNonUnionType(BType type, AnalyzerData TypeTags.TUPLE, TypeTags.READONLY, TypeTags.TYPEDESC -> type; - case TypeTags.JSON -> !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.arrayJsonType : + case TypeTags.JSON -> !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.arrayJsonType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayJsonType, data.env, symTable, anonymousModelHelper, names); - case TypeTags.ANYDATA -> !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.arrayAnydataType : + case TypeTags.ANYDATA -> !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.arrayAnydataType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayAnydataType, data.env, symTable, anonymousModelHelper, names); - case TypeTags.ANY -> !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.arrayAllType : + case TypeTags.ANY -> !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.arrayAllType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayAllType, data.env, symTable, anonymousModelHelper, names); case TypeTags.INTERSECTION -> ((BIntersectionType) type).effectiveType; @@ -1404,7 +1397,7 @@ private BType getBroadType(BType type) { if (type.tag != TypeTags.FINITE) { return type; } - return ((BFiniteType) type).getValueSpace().iterator().next().getBType(); + return singleShapeBroadType(type.semType(), symTable).get(); } private BSymbol getUnaryOpSymbol(BLangUnaryExpr unaryExpr, BType type, AnalyzerData data) { @@ -1422,7 +1415,7 @@ private BSymbol getUnaryOpSymbol(BLangUnaryExpr unaryExpr, BType type, AnalyzerD } if (symbol == symTable.notFoundSymbol) { - exprType = ((BFiniteType) type).getValueSpace().iterator().next().getBType(); + exprType = singleShapeBroadType(type.semType(), symTable).get(); symbol = symResolver.resolveUnaryOperator(unaryExpr.operator, exprType); if (symbol == symTable.notFoundSymbol) { symbol = symResolver.getUnaryOpsForTypeSets(unaryExpr.operator, exprType); @@ -1445,12 +1438,10 @@ private Object calculateSingletonValue(BFiniteType lhs, BFiniteType rhs, Operato return null; } - BLangLiteral lhsLiteral = (BLangLiteral) lhs.getValueSpace().iterator().next(); - BLangLiteral rhsLiteral = (BLangLiteral) rhs.getValueSpace().iterator().next(); - // See Types.isAllowedConstantType() for supported types. - Object lhsValue = getValue(lhsLiteral); - Object rhsValue = getValue(rhsLiteral); + Object lhsValue = Core.singleShape(lhs.semType()).get().value; + Object rhsValue = Core.singleShape(rhs.semType()).get().value; + try { switch (kind) { case ADD: @@ -1498,14 +1489,15 @@ private Object getValue(BLangLiteral lhsLiteral) { private Object evaluateUnaryOperator(BFiniteType finiteType, BType type, OperatorKind kind, AnalyzerData data) { // Calculate the value for the unary operation. - BLangLiteral lhsLiteral = (BLangLiteral) finiteType.getValueSpace().iterator().next(); - Object value = getValue(lhsLiteral); - if (value == null) { + Optional optionalValue = Core.singleShape(finiteType.semType()); + if (optionalValue.isEmpty()) { // This is a compilation error. // This is to avoid NPE exceptions in sub-sequent validations. return null; } + Object value = optionalValue.get().value; + try { switch (kind) { case ADD: @@ -1718,13 +1710,6 @@ private BType setLiteralValueAndGetType(BLangLiteral literalExpr, BType expType, // Get the type matching to the tag from the symbol table. BType literalType = symTable.getTypeFromTag(literalExpr.getBType().tag); - if (literalType.tag == TypeTags.STRING && types.isCharLiteralValue((String) literalValue)) { - boolean foundMember = types.isAssignableToFiniteType(symTable.noType, literalExpr); - if (foundMember) { - setLiteralValueForFiniteType(literalExpr, literalType, data); - return literalType; - } - } return literalType; } @@ -1783,15 +1768,13 @@ private BType getIntegerLiteralTypeUsingExpType(BLangLiteral literalExpr, Object literalExpr.value = String.valueOf(literalValue); return symTable.decimalType; case TypeTags.FINITE: - Set valueSpace = ((BFiniteType) expectedType).getValueSpace(); - if (valueSpace.size() > 1) { - LinkedHashSet memTypes = new LinkedHashSet<>(); - valueSpace.forEach(memExpr -> memTypes.add(memExpr.getBType())); - BUnionType unionType = new BUnionType(null, memTypes, false, false); - return getIntegerLiteralTypeUsingExpType(literalExpr, literalValue, unionType); - } - BType expBroadType = ((BFiniteType) expectedType).getValueSpace().iterator().next().getBType(); - return getIntegerLiteralTypeUsingExpType(literalExpr, literalValue, expBroadType); + Set broadTypes = SemTypeHelper.broadTypes((BFiniteType) expectedType, symTable); + if (broadTypes.size() == 1) { + return getIntegerLiteralTypeUsingExpType(literalExpr, literalValue, broadTypes.iterator().next()); + } + + BUnionType unionType = new BUnionType(types.typeEnv(), null, new LinkedHashSet<>(broadTypes), false); + return getIntegerLiteralTypeUsingExpType(literalExpr, literalValue, unionType); case TypeTags.UNION: BUnionType expectedUnionType = (BUnionType) expectedType; List validTypes = new ArrayList<>(); @@ -1837,12 +1820,6 @@ private BType getIntegerLiteralTypeUsingExpType(BLangLiteral literalExpr, Object return symTable.intType; } - public void setLiteralValueForFiniteType(BLangLiteral literalExpr, BType type, AnalyzerData data) { - types.setImplicitCastExpr(literalExpr, type, data.expType); - data.resultType = type; - literalExpr.isFiniteContext = true; - } - private BType getTypeOfLiteralWithDecimalDiscriminator(BLangLiteral literalExpr, Object literalValue) { literalExpr.value = NumericLiteralSupport.stripDiscriminator(String.valueOf(literalValue)); if (!types.isValidDecimalNumber(literalExpr.pos, literalExpr.value.toString())) { @@ -1880,7 +1857,7 @@ private BType getTypeOfDecimalFloatingPointLiteralUsingExpType(BLangLiteral lite } return symTable.semanticError; case TypeTags.FINITE: - BType expBroadType = ((BFiniteType) expectedType).getValueSpace().iterator().next().getBType(); + BType expBroadType = singleShapeBroadType(expectedType.semType(), symTable).get(); return getTypeOfDecimalFloatingPointLiteralUsingExpType(literalExpr, literalValue, expBroadType); case TypeTags.UNION: BUnionType expectedUnionType = (BUnionType) expectedType; @@ -1921,28 +1898,22 @@ public BType getTypeOfHexFloatingPointLiteral(BLangLiteral literalExpr, Object l return symTable.floatType; } - private BType getFiniteType(Object value, BConstantSymbol constantSymbol, Location pos, BType type) { - return switch (type.tag) { - case TypeTags.INT, - TypeTags.FLOAT, - TypeTags.DECIMAL, - TypeTags.BYTE -> { - BLangNumericLiteral numericLiteral = (BLangNumericLiteral) TreeBuilder.createNumericLiteralExpression(); - yield createFiniteType(constantSymbol, updateLiteral(numericLiteral, value, type, pos)); - } -// case TypeTags.BYTE -> { -// BLangNumericLiteral byteLiteral = (BLangNumericLiteral) TreeBuilder.createNumericLiteralExpression(); -// yield createFiniteType(constantSymbol, updateLiteral(byteLiteral, value, symTable.intType, pos)); -// } - case TypeTags.STRING, - TypeTags.NIL, - TypeTags.BOOLEAN -> { - BLangLiteral literal = (BLangLiteral) TreeBuilder.createLiteralExpression(); - yield createFiniteType(constantSymbol, updateLiteral(literal, value, type, pos)); - } - case TypeTags.UNION -> createFiniteType(constantSymbol, value, (BUnionType) type, pos); - default -> type; - }; + private BType getFiniteType(Object value, BConstantSymbol constantSymbol, BType type) { + switch (type.tag) { + case TypeTags.INT: + case TypeTags.FLOAT: + case TypeTags.DECIMAL: + case TypeTags.BYTE: + case TypeTags.STRING: + case TypeTags.NIL: + case TypeTags.BOOLEAN: + BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, constantSymbol.flags, + Names.EMPTY, constantSymbol.pkgID, null, constantSymbol.owner, constantSymbol.pos, VIRTUAL); + return BFiniteType.newSingletonBFiniteType(finiteTypeSymbol, + SemTypeHelper.resolveSingletonType(value, type.getKind())); + default: + return type; + } } private BLangLiteral getLiteral(Object value, Location pos, BType type) { @@ -1963,38 +1934,8 @@ private BLangLiteral updateLiteral(BLangLiteral literal, Object value, BType typ return literal; } - private BFiniteType createFiniteType(BConstantSymbol constantSymbol, BLangExpression expr) { - BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, constantSymbol.flags, Names.EMPTY, - constantSymbol.pkgID, null, constantSymbol.owner, - constantSymbol.pos, VIRTUAL); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - finiteType.addValue(expr); - finiteType.tsymbol.type = finiteType; - return finiteType; - } - - private BUnionType createFiniteType(BConstantSymbol constantSymbol, Object value, BUnionType type, Location pos) { - LinkedHashSet memberTypes = new LinkedHashSet<>(3); - for (BType memberType : type.getMemberTypes()) { - BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, constantSymbol.flags, - Names.EMPTY, constantSymbol.pkgID, null, constantSymbol.owner, constantSymbol.pos, VIRTUAL); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - Object memberValue = switch (memberType.tag) { - case TypeTags.FLOAT -> value instanceof String ? - Double.parseDouble((String) value) : ((Long) value).doubleValue(); - case TypeTags.DECIMAL -> new BigDecimal(String.valueOf(value)); - default -> value; - }; - finiteType.addValue(getLiteral(memberValue, pos, memberType)); - finiteType.tsymbol.type = finiteType; - memberTypes.add(finiteType); - } - - return BUnionType.create(null, memberTypes); - } - private boolean addFields(LinkedHashMap fields, BType keyValueType, String key, Location pos, - BRecordTypeSymbol recordSymbol) { + BRecordTypeSymbol recordSymbol) { Name fieldName = Names.fromString(key); if (fields.containsKey(key)) { dlog.error(pos, DiagnosticErrorCode.DUPLICATE_KEY_IN_MAPPING_CONSTRUCTOR, TypeKind.RECORD.typeName(), key); @@ -2053,7 +1994,7 @@ private BSymbol getOpSymbolBothUnion(BUnionType lhsType, BUnionType rhsType, lhsType.remove(memberTypeLhs); } if (memberTypes.size() != 1) { - data.resultType = BUnionType.create(null, memberTypes); + data.resultType = BUnionType.create(symTable.typeEnv(), null, memberTypes); } else { data.resultType = memberTypes.iterator().next(); } @@ -2089,7 +2030,7 @@ private BSymbol getOpSymbolLhsUnion(BUnionType lhsType, BType rhsType, lhsType.remove(memberTypeLhs); } if (memberTypes.size() != 1) { - data.resultType = BUnionType.create(null, memberTypes); + data.resultType = BUnionType.create(symTable.typeEnv(), null, memberTypes); } else { data.resultType = memberTypes.iterator().next(); } @@ -2152,7 +2093,7 @@ private BSymbol getOpSymbol(BType lhsType, BType rhsType, BLangBinaryExpr binary /** * @since 2201.7.0 */ - public static class FillMembers implements TypeVisitor { + public static class FillMembers extends TypeVisitor { private static final CompilerContext.Key FILL_MEMBERS_KEY = new CompilerContext.Key<>(); @@ -2190,7 +2131,7 @@ public boolean addFillMembers(BTupleType type, BType expType, AnalyzerData data) int tupleMemberCount = tupleTypes.size(); if (refType.tag == TypeTags.ARRAY) { BArrayType arrayType = (BArrayType) expType; - int noOfFillMembers = arrayType.size - tupleMemberCount; + int noOfFillMembers = arrayType.getSize() - tupleMemberCount; BType fillMemberType = getFillMembers(arrayType.eType, data); if (fillMemberType == symTable.semanticError) { return false; @@ -2236,7 +2177,7 @@ public void visit(BArrayType arrayType) { Flags.asMask(EnumSet.of(Flag.PUBLIC)), Names.EMPTY, data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, null, SOURCE); if (arrayType.state == BArrayState.OPEN) { - BTupleType resultTupleType = new BTupleType(tupleTypeSymbol, new ArrayList<>()); + BTupleType resultTupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, new ArrayList<>()); tupleTypeSymbol.type = resultTupleType; data.resultType = resultTupleType; return; @@ -2249,23 +2190,18 @@ public void visit(BArrayType arrayType) { data.resultType = symTable.semanticError; return; } - List tupleTypes = new ArrayList<>(arrayType.size); - for (int i = 0; i < arrayType.size; i++) { + List tupleTypes = new ArrayList<>(arrayType.getSize()); + for (int i = 0; i < arrayType.getSize(); i++) { tupleTypes.add(fillMemberType); } List members = new ArrayList<>(); tupleTypes.forEach(m -> members.add(new BTupleMember(m, Symbols.createVarSymbolForTupleMember(m)))); - BTupleType resultTupleType = new BTupleType(tupleTypeSymbol, members); + BTupleType resultTupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, members); tupleTypeSymbol.type = resultTupleType; data.resultType = resultTupleType; } - @Override - public void visit(BBuiltInRefType bBuiltInRefType) { - - } - @Override public void visit(BAnyType bAnyType) { data.resultType = symTable.nilType; @@ -2283,8 +2219,7 @@ public void visit(BErrorType bErrorType) { @Override public void visit(BFiniteType finiteType) { - Set valueSpace = finiteType.getValueSpace(); - if (valueSpace.size() > 1) { + if (Core.singleShape(finiteType.semType()).isEmpty()) { if (finiteType.isNullable()) { // Ex. 1|null data.resultType = symTable.nilType; return; @@ -2315,8 +2250,8 @@ public void visit(BJSONType bjsonType) { public void visit(BMapType bMapType) { BRecordTypeSymbol recordSymbol = constantTypeChecker.createRecordTypeSymbol(data.constantSymbol.pkgID, data.constantSymbol.pos, VIRTUAL, data); - recordSymbol.type = new BRecordType(recordSymbol); - BRecordType resultRecordType = new BRecordType(recordSymbol); + recordSymbol.type = new BRecordType(symTable.typeEnv(), recordSymbol); + BRecordType resultRecordType = new BRecordType(symTable.typeEnv(), recordSymbol); recordSymbol.type = resultRecordType; resultRecordType.tsymbol = recordSymbol; resultRecordType.sealed = true; @@ -2352,7 +2287,7 @@ public void visit(BNeverType bNeverType) { } @Override - public void visit(BNilType bNilType) { + public void visitNilType(BType bType) { data.resultType = symTable.nilType; } @@ -2389,7 +2324,7 @@ public void visit(BTupleType tupleType) { List members = new ArrayList<>(); tupleTypes.forEach(m -> members.add(new BTupleMember(m, Symbols.createVarSymbolForTupleMember(m)))); - BTupleType resultTupleType = new BTupleType(tupleTypeSymbol, members); + BTupleType resultTupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, members); tupleTypeSymbol.type = resultTupleType; data.resultType = resultTupleType; } @@ -2410,7 +2345,7 @@ public void visit(BUnionType unionType) { @Override public void visit(BIntersectionType intersectionType) { - data.resultType = getFillMembers(intersectionType.getEffectiveType(), data); + data.resultType = getFillMembers(intersectionType.effectiveType, data); } @Override @@ -2434,7 +2369,7 @@ public void visit(BRecordType recordType) { return; } } - BRecordType resultRecordType = new BRecordType(recordSymbol); + BRecordType resultRecordType = new BRecordType(symTable.typeEnv(), recordSymbol); recordSymbol.type = resultRecordType; resultRecordType.tsymbol = recordSymbol; resultRecordType.sealed = true; @@ -2450,12 +2385,22 @@ public void visit(BObjectType bObjectType) { } @Override - public void visit(BType type) { + public void visit(BType type) { // TODO: Can we get rid of refType switch? + switch (type.tag) { + case TypeTags.NIL: + visitNilType(type); + return; + } + + BConstantSymbol constantSymbol = data.constantSymbol; + BTypeSymbol finiteTypeSym = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, constantSymbol.flags, + Names.EMPTY, constantSymbol.pkgID, null, constantSymbol.owner, constantSymbol.pos, VIRTUAL); + BType refType = Types.getImpliedType(type); switch (refType.tag) { case TypeTags.BOOLEAN: data.resultType = symTable.falseType; - return; + break; case TypeTags.INT: case TypeTags.SIGNED8_INT: case TypeTags.SIGNED16_INT: @@ -2464,24 +2409,21 @@ public void visit(BType type) { case TypeTags.UNSIGNED16_INT: case TypeTags.UNSIGNED32_INT: case TypeTags.BYTE: - data.resultType = constantTypeChecker.getFiniteType(0L, data.constantSymbol, - null, symTable.intType); - return; + data.resultType = BFiniteType.newSingletonBFiniteType(finiteTypeSym, SemTypes.intConst(0)); + break; case TypeTags.FLOAT: - data.resultType = constantTypeChecker.getFiniteType(0.0d, data.constantSymbol, - null, symTable.floatType); - return; + data.resultType = BFiniteType.newSingletonBFiniteType(finiteTypeSym, SemTypes.floatConst(0)); + break; case TypeTags.DECIMAL: - data.resultType = constantTypeChecker.getFiniteType(new BigDecimal(0), data.constantSymbol, - null, symTable.decimalType); - return; + data.resultType = BFiniteType.newSingletonBFiniteType(finiteTypeSym, SemTypes.decimalConst("0")); + break; case TypeTags.STRING: case TypeTags.CHAR_STRING: - data.resultType = constantTypeChecker.getFiniteType("", data.constantSymbol, - null, symTable.stringType); - return; + data.resultType = BFiniteType.newSingletonBFiniteType(finiteTypeSym, SemTypes.stringConst("")); + break; default: data.resultType = symTable.semanticError; + break; } } @@ -2501,11 +2443,10 @@ public BLangConstantValue getConstantValue(BType type) { BType refType = Types.getImpliedType(type); switch (refType.tag) { case TypeTags.FINITE: - BLangExpression expr = ((BFiniteType) refType).getValueSpace().iterator().next(); - if (expr.getBType().tag == TypeTags.DECIMAL) { - return new BLangConstantValue ((((BLangNumericLiteral) expr).value).toString(), expr.getBType()); - } - return new BLangConstantValue (((BLangLiteral) expr).value, expr.getBType()); + BType t = singleShapeBroadType(refType.semType(), symTable).get(); + Value v = Core.singleShape(refType.semType()).get(); + // TODO: 12/9/23 merge t and v to a single object + return new BLangConstantValue (v.value, t); case TypeTags.RECORD: Map fields = new HashMap<>(); LinkedHashMap recordFields = ((BRecordType) refType).fields; @@ -2540,11 +2481,13 @@ public static class ResolveConstantExpressionType extends RESOLVE_CONSTANT_EXPRESSION_TYPE = new CompilerContext.Key<>(); private final Types types; private final ConstantTypeChecker constantTypeChecker; + private final SymbolTable symTable; public ResolveConstantExpressionType(CompilerContext context) { context.put(RESOLVE_CONSTANT_EXPRESSION_TYPE, this); this.types = Types.getInstance(context); this.constantTypeChecker = ConstantTypeChecker.getInstance(context); + this.symTable = SymbolTable.getInstance(context); } public static ResolveConstantExpressionType getInstance(CompilerContext context) { @@ -2599,7 +2542,7 @@ public void visit(BLangLiteral literalExpr, AnalyzerData data) { private void updateBlangExprType(BLangExpression expression, AnalyzerData data) { BType expressionType = expression.getBType(); if (expressionType.tag == TypeTags.FINITE) { - expressionType = ((BFiniteType) expressionType).getValueSpace().iterator().next().getBType(); + expressionType = singleShapeBroadType(expressionType.semType(), symTable).get(); expression.setBType(expressionType); types.setImplicitCastExpr(expression, data.expType, expressionType); return; @@ -2611,13 +2554,13 @@ private void updateBlangExprType(BLangExpression expression, AnalyzerData data) BType targetType; BType expType = data.expType; if (expType.tag == TypeTags.FINITE) { - targetType = ((BFiniteType) expType).getValueSpace().iterator().next().getBType(); + targetType = singleShapeBroadType(expType.semType(), symTable).get(); } else { targetType = expType; } for (BType memberType : ((BUnionType) expressionType).getMemberTypes()) { - BType type = ((BFiniteType) memberType).getValueSpace().iterator().next().getBType(); + BType type = singleShapeBroadType(memberType.semType(), symTable).get(); if (type.tag == targetType.tag || types.isAssignable(memberType, targetType)) { expression.setBType(type); @@ -2671,10 +2614,8 @@ public void visit(BLangRecordLiteral recordLiteral, AnalyzerData data) { (BLangRecordLiteral.BLangRecordKeyValueField) field; BLangRecordLiteral.BLangRecordKey computedKey = computedKeyValue.key; BType fieldName = constantTypeChecker.checkConstExpr(computedKey.expr, data); - BLangLiteral fieldNameLiteral = - (BLangLiteral) ((BFiniteType) fieldName).getValueSpace().iterator().next(); - expFieldType = - getResolvedFieldType(constantTypeChecker.getKeyName(fieldNameLiteral), resolvedType); + expFieldType = getResolvedFieldType(Core.singleShape(fieldName.semType()).get().value, + resolvedType); resolveConstExpr(computedKey.expr, expFieldType, data); resolveConstExpr(keyValueExpr, expFieldType, data); continue; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java index b929c95ad4fe..ecedc5dd1a37 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/ConstantValueResolver.java @@ -698,13 +698,11 @@ private boolean isListOrMapping(int tag) { }; } - private BFiniteType createFiniteType(BConstantSymbol constantSymbol, BLangExpression expr) { + private BFiniteType createFiniteType(BConstantSymbol constantSymbol, BLangLiteral literal) { BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, constantSymbol.flags, Names.EMPTY, constantSymbol.pkgID, null, constantSymbol.owner, constantSymbol.pos, VIRTUAL); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - finiteType.addValue(expr); - return finiteType; + return BFiniteType.newSingletonBFiniteType(finiteTypeSymbol, SemTypeHelper.resolveSingletonType(literal)); } private BType checkType(BLangExpression expr, BConstantSymbol constantSymbol, Object value, BType type, @@ -845,7 +843,7 @@ private BType createRecordType(BLangExpression expr, BConstantSymbol constantSym constantSymbol.pkgID, null, constantSymbol.owner, pos, VIRTUAL); recordTypeSymbol.scope = constantSymbol.scope; - BRecordType recordType = new BRecordType(recordTypeSymbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordTypeSymbol); recordType.tsymbol.name = genName; recordType.sealed = true; recordType.restFieldType = new BNoType(TypeTags.NONE); @@ -860,7 +858,7 @@ private BType createRecordType(BLangExpression expr, BConstantSymbol constantSym createTypeDefinition(recordType, pos, env); updateRecordFields(recordType, pos, env); recordType.tsymbol.flags |= Flags.READONLY; - recordType.flags |= Flags.READONLY; + recordType.addFlags(Flags.READONLY); return recordType; } @@ -1026,8 +1024,8 @@ private BType createTupleType(BLangExpression expr, BConstantSymbol constantSymb Names.EMPTY, env.enclPkg.symbol.pkgID, null, env.scope.owner, pos, VIRTUAL); - return ImmutableTypeCloner.getImmutableIntersectionType(pos, types, new BTupleType(tupleTypeSymbol, tupleTypes), - env, symTable, anonymousModelHelper, names, - new HashSet<>()); + return ImmutableTypeCloner.getImmutableIntersectionType(pos, types, + new BTupleType(symTable.typeEnv(), tupleTypeSymbol, tupleTypes), env, symTable, anonymousModelHelper, + names, new HashSet<>()); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java index 61f0b0af8994..663d32078caf 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/DataflowAnalyzer.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.analyzer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; import org.ballerinalang.compiler.CompilerPhase; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.symbols.SymbolKind; @@ -2738,7 +2739,7 @@ private void checkFinalObjectFieldUpdate(BLangFieldBasedAccess fieldAccess) { BType exprType = Types.getImpliedType(expr.getBType()); - if (types.isSubTypeOfBaseType(exprType, TypeTags.OBJECT) && + if (types.isSubTypeOfBaseType(exprType, PredefinedType.OBJECT) && isFinalFieldInAllObjects(fieldAccess.pos, exprType, fieldAccess.field.value)) { dlog.error(fieldAccess.pos, DiagnosticErrorCode.CANNOT_UPDATE_FINAL_OBJECT_FIELD, fieldAccess.symbol.originalName); @@ -2816,11 +2817,7 @@ private void emitUnusedVariableWarnings(Map unusedLocalVariab } private boolean addVarIfInferredTypeIncludesError(BLangSimpleVariable variable) { - BType typeIntersection = - types.getTypeIntersection(Types.IntersectionContext.compilerInternalIntersectionContext(), - variable.getBType(), symTable.errorType, env); - if (typeIntersection != null && - typeIntersection != symTable.semanticError && typeIntersection != symTable.noType) { + if (types.containsErrorType(variable.getBType().semType())) { unusedErrorVarsDeclaredWithVar.put(variable.symbol, variable.pos); return true; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/EffectiveTypePopulator.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/EffectiveTypePopulator.java index 9d916811e724..4f25fd82f9fd 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/EffectiveTypePopulator.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/EffectiveTypePopulator.java @@ -33,7 +33,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -44,7 +43,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -83,7 +81,7 @@ * * @since 2201.7.0 */ -public class EffectiveTypePopulator implements TypeVisitor { +public class EffectiveTypePopulator extends TypeVisitor { private static final CompilerContext.Key UPDATE_IMMUTABLE_TYPE_KEY = new CompilerContext.Key<>(); @@ -151,11 +149,6 @@ public void visit(BArrayType bArrayType) { } } - @Override - public void visit(BBuiltInRefType bBuiltInRefType) { - - } - @Override public void visit(BAnyType bAnyType) { @@ -226,7 +219,7 @@ public void visit(BNeverType bNeverType) { } @Override - public void visit(BNilType bNilType) { + public void visitNilType(BType bNilType) { } @@ -271,7 +264,7 @@ public void visit(BTupleType bTupleType) { BTypeSymbol tsymbol = bTupleType.tsymbol; if (tsymbol != null && tsymbol.name != null && !tsymbol.name.value.isEmpty() - && !Symbols.isFlagOn(bTupleType.flags, Flags.EFFECTIVE_TYPE_DEF)) { + && !Symbols.isFlagOn(bTupleType.getFlags(), Flags.EFFECTIVE_TYPE_DEF)) { BLangTupleTypeNode tupleTypeNode = (BLangTupleTypeNode) TreeBuilder.createTupleTypeNode(); tupleTypeNode.setBType(bTupleType); BLangTypeDefinition typeDefinition = TypeDefBuilderHelper.addTypeDefinition(bTupleType, @@ -442,11 +435,6 @@ public void visit(BObjectType bObjectType) { } } - @Override - public void visit(BType bType) { - - } - @Override public void visit(BFutureType bFutureType) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsAnydataUniqueVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsAnydataUniqueVisitor.java deleted file mode 100644 index cbbc0d8417df..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsAnydataUniqueVisitor.java +++ /dev/null @@ -1,342 +0,0 @@ -package org.wso2.ballerinalang.compiler.semantics.analyzer; - -import org.wso2.ballerinalang.compiler.semantics.model.UniqueTypeVisitor; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnnotationType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BField; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BHandleType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BIntSubType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BIntersectionType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BParameterizedType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BRecordType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BStreamType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BStructureType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTableType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTupleType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeReferenceType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLSubType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; -import org.wso2.ballerinalang.compiler.util.TypeTags; - -import java.util.HashSet; - -/** - * IsAnydataUniqueVisitor to check if a type is anydata. - * - * This is introduced to handle cyclic unions. - * @since slp4 - */ -public class IsAnydataUniqueVisitor implements UniqueTypeVisitor { - - private final HashSet visited; - private final boolean isAnydata; - - public IsAnydataUniqueVisitor() { - visited = new HashSet<>(); - isAnydata = true; - } - - public IsAnydataUniqueVisitor(HashSet visited) { - this.visited = visited; - isAnydata = true; - } - - private boolean isAnydata(BType type) { - return switch (Types.getImpliedType(type).tag) { - case TypeTags.INT, - TypeTags.BYTE, - TypeTags.FLOAT, - TypeTags.DECIMAL, - TypeTags.STRING, - TypeTags.CHAR_STRING, - TypeTags.BOOLEAN, - TypeTags.JSON, - TypeTags.XML, - TypeTags.XML_TEXT, - TypeTags.XML_ELEMENT, - TypeTags.XML_COMMENT, - TypeTags.XML_PI, - TypeTags.NIL, - TypeTags.NEVER, - TypeTags.ANYDATA, - TypeTags.SIGNED8_INT, - TypeTags.SIGNED16_INT, - TypeTags.SIGNED32_INT, - TypeTags.UNSIGNED8_INT, - TypeTags.UNSIGNED16_INT, - TypeTags.UNSIGNED32_INT, - TypeTags.REGEXP -> true; - default -> false; - }; - } - - @Override - public boolean isVisited(BType type) { - return visited.contains(type); - } - - @Override - public void reset() { - visited.clear(); - } - - @Override - public Boolean visit(BAnnotationType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BArrayType type) { - if (isVisited(type)) { - return isAnydata; - } - visited.add(type); - return visit(type.eType); - } - - @Override - public Boolean visit(BBuiltInRefType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BAnyType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BAnydataType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BErrorType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BInvokableType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BJSONType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BMapType type) { - if (isVisited(type)) { - return isAnydata; - } - visited.add(type); - return visit(type.constraint); - } - - @Override - public Boolean visit(BStreamType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BTypedescType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BParameterizedType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BNeverType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BNilType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BNoType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BPackageType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BStructureType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BTupleType type) { - if (type.isAnyData != null) { - return type.isAnyData; - } - if (!visited.add(type)) { - return isAnydata; - } - for (BType memberType : type.getTupleTypes()) { - if (!visit(memberType)) { - type.isAnyData = false; - return false; - } - } - type.isAnyData = (type.restType == null) || visit(type.restType); - return isAnydata; - } - - @Override - public Boolean visit(BIntersectionType type) { - return visit(type.effectiveType); - } - - @Override - public Boolean visit(BTypeReferenceType type) { - return visit(type.referredType); - } - - @Override - public Boolean visit(BXMLType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BTableType type) { - return visit(type.constraint); - } - - @Override - public Boolean visit(BFiniteType type) { - if (type.isAnyData != null) { - return type.isAnyData; - } - for (BLangExpression value : type.getValueSpace()) { - if (!visit(value.getBType())) { - type.isAnyData = false; - return false; - } - } - type.isAnyData = true; - return isAnydata; - } - - @Override - public Boolean visit(BObjectType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BUnionType type) { - if (type.isAnyData != null) { - return type.isAnyData; - } - if (isVisited(type)) { - return isAnydata; - } - visited.add(type); - for (BType member : type.getMemberTypes()) { - if (!visit(member)) { - type.isAnyData = false; - return false; - } - } - type.isAnyData = isAnydata; - return isAnydata; - } - - @Override - public Boolean visit(BRecordType type) { - if (type.isAnyData != null) { - return type.isAnyData; - } - if (isVisited(type)) { - return isAnydata; - } - visited.add(type); - for (BField field : type.fields.values()) { - if (!visit(field.type)) { - type.isAnyData = false; - return false; - } - } - - if (!type.sealed && (type.restFieldType == null)) { - return false; - } - - type.isAnyData = type.sealed || visit(type.restFieldType); - return type.isAnyData; - } - - @Override - public Boolean visit(BType type) { - return switch (type.tag) { - case TypeTags.TABLE -> visit((BTableType) type); - case TypeTags.ANYDATA -> visit((BAnydataType) type); - case TypeTags.RECORD -> visit((BRecordType) type); - case TypeTags.ARRAY -> visit((BArrayType) type); - case TypeTags.UNION -> visit((BUnionType) type); - case TypeTags.TYPEDESC -> visit((BTypedescType) type); - case TypeTags.MAP -> visit((BMapType) type); - case TypeTags.FINITE -> visit((BFiniteType) type); - case TypeTags.TUPLE -> visit((BTupleType) type); - case TypeTags.INTERSECTION -> visit((BIntersectionType) type); - case TypeTags.TYPEREFDESC -> visit((BTypeReferenceType) type); - case TypeTags.SIGNED8_INT, - TypeTags.SIGNED16_INT, - TypeTags.SIGNED32_INT, - TypeTags.UNSIGNED8_INT, - TypeTags.UNSIGNED16_INT, - TypeTags.UNSIGNED32_INT -> visit((BIntSubType) type); - case TypeTags.XML_ELEMENT, - TypeTags.XML_PI, - TypeTags.XML_COMMENT, - TypeTags.XML_TEXT -> visit((BXMLSubType) type); - default -> isAnydata(type); - }; - } - - @Override - public Boolean visit(BFutureType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BHandleType type) { - return isAnydata(type); - } - - @Override - public Boolean visit(BIntSubType bHandleType) { - return true; - } - - @Override - public Boolean visit(BXMLSubType bxmlSubType) { - return true; - } -} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsPureTypeUniqueVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsPureTypeUniqueVisitor.java deleted file mode 100644 index ca6528a68796..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsPureTypeUniqueVisitor.java +++ /dev/null @@ -1,295 +0,0 @@ -package org.wso2.ballerinalang.compiler.semantics.analyzer; - -import org.wso2.ballerinalang.compiler.semantics.model.UniqueTypeVisitor; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnnotationType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BHandleType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BIntSubType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BIntersectionType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BParameterizedType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BRecordType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BStreamType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BStructureType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTableType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTupleType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeReferenceType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLSubType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.util.TypeTags; - -import java.util.HashSet; - -/** - * IsPureTypeUniqueVisitor to check if a type is pure data. - * - * This is introduced to handle cyclic unions. - * @since slp4 - */ -public class IsPureTypeUniqueVisitor implements UniqueTypeVisitor { - - private final HashSet visited; - private final boolean isPureType; - - public IsPureTypeUniqueVisitor() { - visited = new HashSet<>(); - isPureType = true; - } - - public IsPureTypeUniqueVisitor(HashSet visited) { - this.visited = visited; - isPureType = true; - } - - private boolean isAnyData(BType type) { - return switch (Types.getImpliedType(type).tag) { - case TypeTags.INT, - TypeTags.BYTE, - TypeTags.FLOAT, - TypeTags.DECIMAL, - TypeTags.STRING, - TypeTags.BOOLEAN, - TypeTags.JSON, - TypeTags.XML, - TypeTags.XML_TEXT, - TypeTags.TABLE, - TypeTags.NIL, - TypeTags.NEVER, - TypeTags.ANYDATA, - TypeTags.SIGNED8_INT, - TypeTags.SIGNED16_INT, - TypeTags.SIGNED32_INT, - TypeTags.UNSIGNED8_INT, - TypeTags.UNSIGNED16_INT, - TypeTags.UNSIGNED32_INT, - TypeTags.CHAR_STRING -> true; - default -> false; - }; - } - - @Override - public boolean isVisited(BType type) { - return visited.contains(type); - } - - @Override - public void reset() { - visited.clear(); - } - - @Override - public Boolean visit(BAnnotationType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BArrayType type) { - if (isVisited(type)) { - return isPureType; - } - visited.add(type); - return visit(type.eType); - } - - @Override - public Boolean visit(BBuiltInRefType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BAnyType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BAnydataType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BErrorType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BFiniteType type) { - IsAnydataUniqueVisitor isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(visited); - return isAnydataUniqueVisitor.visit(type); - } - - @Override - public Boolean visit(BInvokableType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BJSONType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BMapType type) { - if (isVisited(type)) { - return isPureType; - } - visited.add(type); - return visit(type.constraint); - } - - @Override - public Boolean visit(BStreamType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BTypedescType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BParameterizedType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BNeverType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BNilType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BNoType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BPackageType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BStructureType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BTupleType type) { - IsAnydataUniqueVisitor isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(visited); - return isAnydataUniqueVisitor.visit(type); - } - - @Override - public Boolean visit(BUnionType type) { - if (type.isPureType != null) { - return type.isPureType; - } - if (isVisited(type)) { - return isPureType; - } - visited.add(type); - for (BType member : type.getMemberTypes()) { - if (!visit(member)) { - type.isPureType = false; - return false; - } - } - type.isPureType = isPureType; - return isPureType; - } - - @Override - public Boolean visit(BIntersectionType type) { - return visit(type.effectiveType); - } - - @Override - public Boolean visit(BTypeReferenceType type) { - return visit(type.referredType); - } - - @Override - public Boolean visit(BXMLType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BTableType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BRecordType type) { - - IsAnydataUniqueVisitor isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(visited); - return isAnydataUniqueVisitor.visit(type); - } - - @Override - public Boolean visit(BObjectType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BType type) { - return switch (type.tag) { - case TypeTags.TABLE -> visit((BTableType) type); - case TypeTags.ANYDATA -> visit((BAnydataType) type); - case TypeTags.RECORD -> visit((BRecordType) type); - case TypeTags.ARRAY -> visit((BArrayType) type); - case TypeTags.UNION -> visit((BUnionType) type); - case TypeTags.TYPEDESC -> visit((BTypedescType) type); - case TypeTags.MAP -> visit((BMapType) type); - case TypeTags.FINITE -> visit((BFiniteType) type); - case TypeTags.TUPLE -> visit((BTupleType) type); - case TypeTags.INTERSECTION -> visit((BIntersectionType) type); - case TypeTags.TYPEREFDESC -> visit((BTypeReferenceType) type); - case TypeTags.SIGNED8_INT, - TypeTags.SIGNED16_INT, - TypeTags.SIGNED32_INT, - TypeTags.UNSIGNED8_INT, - TypeTags.UNSIGNED16_INT, - TypeTags.UNSIGNED32_INT -> visit((BIntSubType) type); - default -> isAnyData(type); - }; - } - - @Override - public Boolean visit(BFutureType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BHandleType type) { - return isAnyData(type); - } - - @Override - public Boolean visit(BIntSubType type) { - return true; - } - - @Override - public Boolean visit(BXMLSubType bxmlSubType) { - return isAnyData(bxmlSubType); - } -} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java index 298891dc56c8..182ec315066f 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/IsolationAnalyzer.java @@ -50,7 +50,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -61,7 +60,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -359,7 +357,7 @@ public void visit(BLangPackage pkgNode) { } for (BLangClassDefinition classDefinition : pkgNode.classDefinitions) { - if (classDefinition.flagSet.contains(Flag.ANONYMOUS) && isIsolated(classDefinition.getBType().flags)) { + if (classDefinition.flagSet.contains(Flag.ANONYMOUS) && isIsolated(classDefinition.getBType().getFlags())) { // If this is a class definition for an object constructor expression, and the type is `isolated`, // that is due to the expected type being an `isolated` object. We now mark the class definition also // as `isolated`, to enforce the isolation validation. @@ -1516,7 +1514,7 @@ private boolean checkStrandAnnotationExists(List atta private boolean isValidIsolatedAsyncInvocation(BLangInvocation.BLangActionInvocation actionInvocation) { boolean isIsolatedStartAction = true; - if (!isIsolated(actionInvocation.symbol.type.flags)) { + if (!isIsolated(actionInvocation.symbol.type.getFlags())) { dlog.error(actionInvocation.name.pos, DiagnosticErrorCode.INVALID_ASYNC_INVOCATION_OF_NON_ISOLATED_FUNCTION_IN_ISOLATED_FUNCTION); isIsolatedStartAction = false; @@ -2114,7 +2112,7 @@ private void analyzeInvocation(BLangInvocation invocationExpr) { boolean expectsIsolation = inIsolatedFunction || recordFieldDefaultValue || objectFieldDefaultValueRequiringIsolation; - boolean isolatedFunctionCall = isIsolated(symbol.type.flags); + boolean isolatedFunctionCall = isIsolated(symbol.type.getFlags()); boolean inStartAction = invocationExpr.async && !invocationExpr.functionPointerInvocation; @@ -2168,7 +2166,7 @@ private void analyzeInvocation(BLangInvocation invocationExpr) { private void markFunctionDependentlyIsolatedOnStartAction(BInvokableSymbol enclInvokableSymbol, Set argsList, BInvokableSymbol symbol) { - boolean isIsolatedFunction = isIsolated(symbol.type.flags); + boolean isIsolatedFunction = isIsolated(symbol.type.getFlags()); if (!isIsolatedFunction && Symbols.isFlagOn(symbol.flags, Flags.PUBLIC)) { markDependsOnIsolationNonInferableConstructs(); return; @@ -2249,9 +2247,10 @@ private void analyzeAndSetArrowFuncFlagForIsolatedParamArg(BLangExpression arg) tsymbol.pkgID, null, tsymbol.owner, tsymbol.pos, tsymbol.origin); dupInvokableTypeSymbol.params = tsymbol.params == null ? null : new ArrayList<>(tsymbol.params); - BInvokableType dupInvokableType = new BInvokableType(invokableType.paramTypes, invokableType.restType, - invokableType.retType, dupInvokableTypeSymbol); - dupInvokableType.flags |= Flags.ISOLATED; + BInvokableType dupInvokableType = + new BInvokableType(symTable.typeEnv(), invokableType.paramTypes, invokableType.restType, + invokableType.retType, dupInvokableTypeSymbol); + dupInvokableType.addFlags(Flags.ISOLATED); dupInvokableTypeSymbol.type = dupInvokableType; argExpr.setBType(dupInvokableType); @@ -2472,16 +2471,16 @@ private BTupleType getRepresentativeTupleTypeForRemainingArgs(int paramCount, in members.add(new BTupleMember(eType, Symbols.createVarSymbolForTupleMember(eType))); } - if (arrayType.size > remReqArgCount) { - return new BTupleType(null, members, eType, 0); + if (arrayType.getSize() > remReqArgCount) { + return new BTupleType(symTable.typeEnv(), null, members, eType, 0); } - return new BTupleType(members); + return new BTupleType(symTable.typeEnv(), members); } private void analyzeRestArgsForRestParam(BLangInvocation invocationExpr, List restArgs, BInvokableSymbol symbol, boolean expectsIsolation) { - if (Symbols.isFlagOn(((BArrayType) symbol.restParam.type).eType.flags, Flags.ISOLATED)) { + if (Symbols.isFlagOn(((BArrayType) symbol.restParam.type).eType.getFlags(), Flags.ISOLATED)) { for (BLangExpression restArg : restArgs) { analyzeNode(restArg, env); } @@ -2543,7 +2542,7 @@ private void analyzeVarArgIsolatedness(BLangInvocation invocationExpr, BLangRest private void handleNonExplicitlyIsolatedArgForIsolatedParam(BLangInvocation invocationExpr, BLangExpression expr, boolean expectsIsolation, BType type, Location pos) { - if (Symbols.isFlagOn(type.flags, Flags.ISOLATED)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.ISOLATED)) { return; } @@ -2579,7 +2578,7 @@ private boolean isInIsolatedFunction(BLangInvokableNode enclInvokable) { if (isNotInArrowFunctionBody(env)) { return false; } - return isIsolated(((BLangArrowFunction) env.enclEnv.node).funcType.flags); + return isIsolated(((BLangArrowFunction) env.enclEnv.node).funcType.getFlags()); } return isIsolated(enclInvokable.symbol.flags); @@ -3016,7 +3015,7 @@ private boolean isIsolatedExpression(BLangExpression expression, boolean logErro return isIsolatedExpression(argExprs.get(0), logErrors, visitRestOnError, nonIsolatedExpressions, inferring, publiclyExposedObjectTypes, classDefinitions, moduleLevelVariables, unresolvedSymbols); - } else if (isIsolated(invocationSymbol.type.flags) || + } else if (isIsolated(invocationSymbol.type.getFlags()) || (inferring && this.isolationInferenceInfoMap.containsKey(invocationSymbol) && inferFunctionIsolation(invocationSymbol, this.isolationInferenceInfoMap.get(invocationSymbol), publiclyExposedObjectTypes, @@ -3258,7 +3257,7 @@ private boolean isSelfOfObject(BLangSimpleVarRef varRefExpr) { } private boolean isSelfOfIsolatedObject(BLangSimpleVarRef varRefExpr) { - return isSelfOfObject(varRefExpr) && isIsolated(varRefExpr.symbol.type.flags); + return isSelfOfObject(varRefExpr) && isIsolated(varRefExpr.symbol.type.getFlags()); } private boolean hasRefDefinedOutsideLock(BLangExpression variableReference) { @@ -3351,7 +3350,7 @@ private boolean isInIsolatedObjectMethod(SymbolEnv env, boolean ignoreInit) { BType ownerType = Types.getImpliedType(enclFunction.symbol.owner.type); - return ownerType.tag == TypeTags.OBJECT && isIsolated(ownerType.flags); + return ownerType.tag == TypeTags.OBJECT && isIsolated(ownerType.getFlags()); } private BLangFunction getEnclNonAnonymousFunction(BLangFunction enclFunction) { @@ -3684,7 +3683,7 @@ private boolean isVarRequiringInference(BSymbol moduleLevelVarSymbol) { } BType type = moduleLevelVarSymbol.type; - return !types.isInherentlyImmutableType(type) && !Symbols.isFlagOn(type.flags, Flags.READONLY); + return !types.isInherentlyImmutableType(type) && !Symbols.isFlagOn(type.getFlags(), Flags.READONLY); } private void populateInferableClass(BLangClassDefinition classDefinition) { @@ -3694,7 +3693,7 @@ private void populateInferableClass(BLangClassDefinition classDefinition) { } BType type = classDefinition.getBType(); - if (Symbols.isFlagOn(type.flags, Flags.ISOLATED)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.ISOLATED)) { return; } @@ -3850,7 +3849,7 @@ private void inferIsolation(Set moduleLevelVarSymbols, Set publi symbol.flags |= Flags.ISOLATED; if (!moduleLevelVarSymbols.contains(symbol)) { - symbol.type.flags |= Flags.ISOLATED; + symbol.type.addFlags(Flags.ISOLATED); } } continue; @@ -3873,7 +3872,7 @@ private void inferIsolation(Set moduleLevelVarSymbols, Set publi symbol.flags |= Flags.ISOLATED; if (isObjectType) { - symbol.type.flags |= Flags.ISOLATED; + symbol.type.addFlags(Flags.ISOLATED); } } } @@ -4184,7 +4183,7 @@ private void logServiceIsolationHints(List classDefinition } private void logServiceIsolationHints(BLangClassDefinition classDefinition) { - boolean isolatedService = isIsolated(classDefinition.getBType().flags); + boolean isolatedService = isIsolated(classDefinition.getBType().getFlags()); for (BLangFunction function : classDefinition.functions) { Set flagSet = function.flagSet; @@ -4193,7 +4192,7 @@ private void logServiceIsolationHints(BLangClassDefinition classDefinition) { continue; } - boolean isolatedMethod = isIsolated(function.getBType().flags); + boolean isolatedMethod = isIsolated(function.getBType().getFlags()); if (isolatedService && isolatedMethod) { continue; @@ -4307,7 +4306,7 @@ private class TemporaryArrowFunctionSymbol extends BInvokableSymbol { } } - private static class BPubliclyExposedInferableTypeCollector implements TypeVisitor { + private static class BPubliclyExposedInferableTypeCollector extends TypeVisitor { Set unresolvedTypes; Set exposedTypes; @@ -4338,10 +4337,6 @@ public void visit(BArrayType bArrayType) { visitType(bArrayType.eType); } - @Override - public void visit(BBuiltInRefType bBuiltInRefType) { - } - @Override public void visit(BAnyType bAnyType) { } @@ -4361,7 +4356,7 @@ public void visit(BFiniteType bFiniteType) { @Override public void visit(BInvokableType bInvokableType) { - if (Symbols.isFlagOn(bInvokableType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(bInvokableType.getFlags(), Flags.ANY_FUNCTION)) { return; } @@ -4406,7 +4401,7 @@ public void visit(BNeverType bNeverType) { } @Override - public void visit(BNilType bNilType) { + public void visitNilType(BType bType) { } @Override @@ -4479,10 +4474,6 @@ public void visit(BObjectType bObjectType) { } } - @Override - public void visit(BType bType) { - } - @Override public void visit(BFutureType bFutureType) { visitType(bFutureType.constraint); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java index dd92270be20c..73fea76ef339 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.analyzer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.clauses.OrderKeyNode; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.symbols.SymbolOrigin; @@ -195,7 +196,7 @@ public void checkQueryType(BLangQueryExpr queryExpr, TypeChecker.AnalyzerData da List collectionTypes = getCollectionTypes(clauses); BType completionType = getCompletionType(collectionTypes, Types.QueryConstructType.DEFAULT, data); if (completionType != null) { - queryType = BUnionType.create(null, queryType, completionType); + queryType = BUnionType.create(symTable.typeEnv(), null, queryType, completionType); } queryExpr.setDeterminedType(queryType); actualType = types.checkType(finalClauseExpr.pos, queryType, data.expType, @@ -302,15 +303,15 @@ public BType resolveQueryType(SymbolEnv env, BLangExpression selectExp, BType ta BType completionType = getCompletionType(collectionTypes, types.getQueryConstructType(queryExpr), data); if (queryExpr.isStream) { - return new BStreamType(TypeTags.STREAM, selectType, completionType, null); + return new BStreamType(symTable.typeEnv(), TypeTags.STREAM, selectType, completionType, null); } else if (queryExpr.isTable) { actualType = getQueryTableType(queryExpr, selectType, resolvedTypes.get(0), env); } else if (queryExpr.isMap) { BType mapConstraintType = getTypeOfTypeParameter(selectType, queryExpr.getSelectClause().expression.pos); if (mapConstraintType != symTable.semanticError) { - actualType = new BMapType(TypeTags.MAP, mapConstraintType, null); - if (Symbols.isFlagOn(resolvedTypes.get(0).flags, Flags.READONLY)) { + actualType = new BMapType(symTable.typeEnv(), TypeTags.MAP, mapConstraintType, null); + if (Symbols.isFlagOn(resolvedTypes.get(0).getFlags(), Flags.READONLY)) { actualType = ImmutableTypeCloner.getImmutableIntersectionType(null, types, actualType, env, symTable, anonymousModelHelper, names, null); } @@ -320,7 +321,8 @@ public BType resolveQueryType(SymbolEnv env, BLangExpression selectExp, BType ta } if (completionType != null && completionType.tag != TypeTags.NIL) { - return BUnionType.create(null, actualType, types.getSafeType(completionType, true, false)); + return BUnionType.create(symTable.typeEnv(), null, actualType, + types.getSafeType(completionType, true, false)); } else { return actualType; } @@ -349,7 +351,7 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel errorTypes.add(elementType); continue; } - BType queryResultType = new BArrayType(selectType); + BType queryResultType = new BArrayType(symTable.typeEnv(), selectType); resolvedType = getResolvedType(queryResultType, type, isReadonly, env); break; case TypeTags.TABLE: @@ -380,7 +382,7 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel BType memberType = ((BMapType) type).getConstraint(); BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(memberType); memberTypeList.add(new BTupleMember(memberType, varSymbol)); - BTupleType newExpType = new BTupleType(null, memberTypeList); + BTupleType newExpType = new BTupleType(symTable.typeEnv(), memberTypeList); selectType = checkExprSilent(selectExp, env, newExpType, data); if (selectType == symTable.semanticError) { errorTypes.add(newExpType); @@ -414,7 +416,7 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel case TypeTags.INTERSECTION: type = ((BIntersectionType) type).effectiveType; solveSelectTypeAndResolveType(queryExpr, selectExp, List.of(type), collectionType, selectTypes, - resolvedTypes, env, data, Symbols.isFlagOn(type.flags, Flags.READONLY)); + resolvedTypes, env, data, Symbols.isFlagOn(type.getFlags(), Flags.READONLY)); return; case TypeTags.NONE: default: @@ -472,7 +474,7 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel BType actualQueryType = silentTypeCheckExpr(queryExpr, symTable.noType, data); if (actualQueryType != symTable.semanticError) { types.checkType(queryExpr, actualQueryType, - BUnionType.create(null, new LinkedHashSet<>(expTypes))); + BUnionType.create(symTable.typeEnv(), null, new LinkedHashSet<>(expTypes))); errorTypes.forEach(expType -> { if (expType.tag == TypeTags.UNION) { checkExpr(nodeCloner.cloneNode(selectExp), env, expType, data); @@ -491,14 +493,14 @@ void solveSelectTypeAndResolveType(BLangQueryExpr queryExpr, BLangExpression sel } private BType getQueryTableType(BLangQueryExpr queryExpr, BType constraintType, BType resolvedType, SymbolEnv env) { - final BTableType tableType = new BTableType(TypeTags.TABLE, constraintType, null); + final BTableType tableType = new BTableType(symTable.typeEnv(), constraintType, null); if (!queryExpr.fieldNameIdentifierList.isEmpty()) { validateKeySpecifier(queryExpr.fieldNameIdentifierList, constraintType); markReadOnlyForConstraintType(constraintType); tableType.fieldNameList = queryExpr.fieldNameIdentifierList.stream() .map(identifier -> ((BLangIdentifier) identifier).value).toList(); } - if (Symbols.isFlagOn(resolvedType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(resolvedType.getFlags(), Flags.READONLY)) { return ImmutableTypeCloner.getImmutableIntersectionType(null, types, tableType, env, symTable, anonymousModelHelper, names, null); } @@ -528,7 +530,7 @@ private void markReadOnlyForConstraintType(BType constraintType) { } } if (recordType.sealed) { - recordType.flags |= Flags.READONLY; + recordType.addFlags(Flags.READONLY); recordType.tsymbol.flags |= Flags.READONLY; } } @@ -549,7 +551,7 @@ private BType getTypeOfTypeParameter(BType selectType, Location pos) { } memberTypes.add(mapType); } - return new BUnionType(null, memberTypes, false, false); + return new BUnionType(types.typeEnv(), null, memberTypes, false); } else { return getQueryMapConstraintType(referredType, pos); } @@ -558,7 +560,7 @@ private BType getTypeOfTypeParameter(BType selectType, Location pos) { private BType getQueryMapConstraintType(BType type, Location pos) { if (type.tag == TypeTags.ARRAY) { BArrayType arrayType = (BArrayType) type; - if (arrayType.state != BArrayState.OPEN && arrayType.size == 2 && + if (arrayType.state != BArrayState.OPEN && arrayType.getSize() == 2 && types.isAssignable(arrayType.eType, symTable.stringType)) { return arrayType.eType; } @@ -632,7 +634,7 @@ private BType getCompletionType(List collectionTypes, Types.QueryConstruc if (completionTypes.size() == 1) { completionType = completionTypes.iterator().next(); } else { - completionType = BUnionType.create(null, completionTypes.toArray(new BType[0])); + completionType = BUnionType.create(symTable.typeEnv(), null, completionTypes.toArray(new BType[0])); } } return completionType; @@ -647,7 +649,7 @@ private List getCollectionTypes(List clauses) { private BType getResolvedType(BType initType, BType expType, boolean isReadonly, SymbolEnv env) { if (initType.tag != TypeTags.SEMANTIC_ERROR && (isReadonly || - Symbols.isFlagOn(expType.flags, Flags.READONLY))) { + Symbols.isFlagOn(expType.getFlags(), Flags.READONLY))) { return ImmutableTypeCloner.getImmutableIntersectionType(null, types, initType, env, symTable, anonymousModelHelper, names, null); } @@ -670,19 +672,19 @@ private BType getNonContextualQueryType(BType constraintType, BType basicType, L dlog.error(pos, INVALID_QUERY_CONSTRUCT_INFERRED_MAP); return symTable.semanticError; case TypeTags.XML: - if (types.isSubTypeOfBaseType(constraintType, symTable.xmlType.tag)) { + if (types.isSubTypeOfBaseType(constraintType, PredefinedType.XML)) { return new BXMLType(constraintType, null); } break; case TypeTags.STRING: - if (types.isSubTypeOfBaseType(constraintType, TypeTags.STRING)) { + if (types.isSubTypeOfBaseType(constraintType, PredefinedType.STRING)) { return symTable.stringType; } break; case TypeTags.ARRAY: case TypeTags.TUPLE: case TypeTags.OBJECT: - return new BArrayType(constraintType); + return new BArrayType(symTable.typeEnv(), constraintType); default: return symTable.semanticError; } @@ -743,24 +745,26 @@ private void handleInputClauseVariables(BLangInputClause bLangInputClause, Symbo BLangVariable variableNode = (BLangVariable) bLangInputClause.variableDefinitionNode.getVariable(); // Check whether the foreach node's variables are declared with var. + BType inputClauseVarType = bLangInputClause.varType; if (bLangInputClause.isDeclaredWithVar) { // If the foreach node's variables are declared with var, type is `varType`. - semanticAnalyzer.handleDeclaredVarInForeach(variableNode, bLangInputClause.varType, blockEnv); + semanticAnalyzer.handleDeclaredVarInForeach(variableNode, inputClauseVarType, blockEnv); return; } // If the type node is available, we get the type from it. BType typeNodeType = symResolver.resolveTypeNode(variableNode.typeNode, blockEnv); // Then we need to check whether the RHS type is assignable to LHS type. - if (types.isAssignable(bLangInputClause.varType, typeNodeType)) { - // If assignable, we set types to the variables. - semanticAnalyzer.handleDeclaredVarInForeach(variableNode, bLangInputClause.varType, blockEnv); - return; - } - // Log an error and define a symbol with the node's type to avoid undeclared symbol errors. - if (typeNodeType != symTable.semanticError) { + if (inputClauseVarType.tag != TypeTags.SEMANTIC_ERROR) { + if (types.isAssignable(inputClauseVarType, typeNodeType)) { + // If assignable, we set types to the variables. + semanticAnalyzer.handleDeclaredVarInForeach(variableNode, inputClauseVarType, blockEnv); + return; + } + // Log an error and define a symbol with the node's type to avoid undeclared symbol errors. dlog.error(variableNode.typeNode.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, - bLangInputClause.varType, typeNodeType); + inputClauseVarType, typeNodeType); } + semanticAnalyzer.handleDeclaredVarInForeach(variableNode, typeNodeType, blockEnv); } @@ -867,7 +871,8 @@ public void visit(BLangCollectClause collectClause, TypeChecker.AnalyzerData dat Name name = new Name(var); BSymbol originalSymbol = symResolver.lookupSymbolInMainSpace(collectEnv, name); BSequenceSymbol sequenceSymbol = new BSequenceSymbol(originalSymbol.flags, name, originalSymbol.pkgID, - new BSequenceType(originalSymbol.getType()), originalSymbol.owner, originalSymbol.pos); + new BSequenceType(symTable.typeEnv(), originalSymbol.getType()), originalSymbol.owner, + originalSymbol.pos); collectEnv.scope.define(name, sequenceSymbol); } } @@ -890,7 +895,7 @@ public void visit(BLangOnConflictClause onConflictClause, TypeChecker.AnalyzerDa if (data.queryData.completeEarlyErrorList != null) { BType possibleErrorType = type.tag == TypeTags.UNION ? types.getErrorType((BUnionType) type) : - types.getErrorType(BUnionType.create(null, type)); + types.getErrorType(BUnionType.create(symTable.typeEnv(), null, type)); data.queryData.completeEarlyErrorList.add(possibleErrorType); } } @@ -910,7 +915,7 @@ public void visit(BLangOrderByClause orderByClause, TypeChecker.AnalyzerData dat orderByClause.env = data.commonAnalyzerData.queryEnvs.peek(); for (OrderKeyNode orderKeyNode : orderByClause.getOrderKeyList()) { BType exprType = checkExpr((BLangExpression) orderKeyNode.getOrderKey(), orderByClause.env, data); - if (!types.isOrderedType(exprType, false)) { + if (exprType.tag != TypeTags.SEMANTIC_ERROR && !types.isOrderedType(exprType)) { dlog.error(((BLangOrderKey) orderKeyNode).expression.pos, DiagnosticErrorCode.ORDER_BY_NOT_SUPPORTED); } } @@ -945,7 +950,8 @@ public void visit(BLangGroupByClause groupByClause, TypeChecker.AnalyzerData dat Name name = new Name(var); BSymbol originalSymbol = symResolver.lookupSymbolInMainSpace(groupByEnv, name); BSequenceSymbol sequenceSymbol = new BSequenceSymbol(originalSymbol.flags, name, originalSymbol.pkgID, - new BSequenceType(originalSymbol.getType()), originalSymbol.owner, originalSymbol.pos); + new BSequenceType(symTable.typeEnv(), originalSymbol.getType()), originalSymbol.owner, + originalSymbol.pos); groupByEnv.scope.define(name, sequenceSymbol); } } @@ -1080,7 +1086,7 @@ public void visit(BLangCollectContextInvocation collectContextInvocation, TypeCh BLangInvocation invocation = collectContextInvocation.invocation; data.resultType = checkExpr(invocation, data.env, data); if (isNilReturnInvocationInCollectClause(invocation, data)) { - data.resultType = BUnionType.create(null, data.resultType, symTable.nilType); + data.resultType = BUnionType.create(symTable.typeEnv(), null, data.resultType, symTable.nilType); } collectContextInvocation.setBType(data.resultType); } @@ -1149,7 +1155,8 @@ public void visit(BLangSimpleVarRef varRefExpr, TypeChecker.AnalyzerData data) { dlog.error(varRefExpr.pos, DiagnosticErrorCode.VARIABLE_IS_SEQUENCED_MORE_THAN_ONCE, varName); } } else if ((symbol.tag & SymTag.TYPE_DEF) == SymTag.TYPE_DEF) { - actualType = symbol.type.tag == TypeTags.TYPEDESC ? symbol.type : new BTypedescType(symbol.type, null); + actualType = symbol.type.tag == TypeTags.TYPEDESC ? symbol.type : + new BTypedescType(symTable.typeEnv(), symbol.type, null); varRefExpr.symbol = symbol; } else if ((symbol.tag & SymTag.CONSTANT) == SymTag.CONSTANT) { BConstantSymbol constSymbol = (BConstantSymbol) symbol; @@ -1199,7 +1206,8 @@ public void visit(BLangListConstructorExpr listConstructor, TypeChecker.Analyzer checkExpr(expr, data.env, symTable.noType, data); data.queryData.withinSequenceContext = false; data.resultType = types.checkType(listConstructor.pos, - new BTupleType(null, new ArrayList<>(0), ((BSequenceType) type).elementType, 0), + new BTupleType(symTable.typeEnv(), null, new ArrayList<>(0), + ((BSequenceType) type).elementType, 0), expType, DiagnosticErrorCode.INCOMPATIBLE_TYPES); listConstructor.setBType(data.resultType); return; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemTypeHelper.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemTypeHelper.java new file mode 100644 index 000000000000..c1339a43921e --- /dev/null +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemTypeHelper.java @@ -0,0 +1,263 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.ballerinalang.compiler.semantics.analyzer; + +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Context; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; +import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; +import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; +import org.wso2.ballerinalang.compiler.semantics.model.types.BType; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; + +import java.math.BigDecimal; +import java.util.LinkedHashSet; +import java.util.Optional; +import java.util.Set; + +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.Core.widenToBasicTypes; + +/** + * Contains helper methods related to sem-types. + * + * @since 2201.9.0 + */ +public final class SemTypeHelper { + + private SemTypeHelper() { + } + + public static SemType resolveSingletonType(BLangLiteral literal) { + return resolveSingletonType(literal.value, literal.getDeterminedType().getKind()); + } + + public static SemType resolveSingletonType(Object value, TypeKind targetTypeKind) { + switch (targetTypeKind) { + case FLOAT: + double doubleVal; + if (value instanceof Long) { + doubleVal = ((Long) value).doubleValue(); + } else if (value instanceof Double) { + doubleVal = (double) value; + } else { + // literal value will be a string if it wasn't within the bounds of what is supported by Java Long + // or Double when it was parsed in BLangNodeBuilder. + try { + doubleVal = Double.parseDouble((String) value); + } catch (NumberFormatException e) { + // We reach here when there is a syntax error. Mock the flow with default float value. + return FloatSubtype.floatConst(0); + } + } + return SemTypes.floatConst(doubleVal); + case INT: + case BYTE: + return SemTypes.intConst(((Number) value).longValue()); + case STRING: + return SemTypes.stringConst((String) value); + case BOOLEAN: + return SemTypes.booleanConst((Boolean) value); + case DECIMAL: + return SemTypes.decimalConst((String) value); + case NIL: + return PredefinedType.NIL; + case OTHER: + // We reach here when there is a semantic error + return PredefinedType.NEVER; + default: + throw new UnsupportedOperationException("Finite type not implemented for: " + targetTypeKind); + } + } + + public static boolean isSubtypeSimple(BType bt, BasicTypeBitSet bbs) { + return SemTypes.isSubtypeSimple(bt.semType(), bbs); + } + + public static boolean isSubtypeSimpleNotNever(BType bt, BasicTypeBitSet bbs) { + return SemTypes.isSubtypeSimpleNotNever(bt.semType(), bbs); + } + + public static boolean containsBasicType(BType bt, BasicTypeBitSet bbs) { + return SemTypes.containsBasicType(bt.semType(), bbs); + } + + public static boolean containsType(Context ctx, BType bt, SemType bbs) { + return SemTypes.containsType(ctx, bt.semType(), bbs); + } + + public static boolean isSubtype(Context context, BType bt, SemType st) { + return SemTypes.isSubtype(context, bt.semType(), st); + } + + public static boolean isSimpleOrString(TypeKind kind) { + switch (kind) { + case NIL: + case BOOLEAN: + case INT: + case BYTE: + case FLOAT: + case DECIMAL: + case STRING: + case FINITE: + return true; + default: + return false; + } + } + + /** + * Returns the basic type of singleton. + *

+ * This will replace the existing finiteType.getValueSpace().iterator().next().getBType() calls + * + * @param t SemType component of BFiniteType + */ + public static Optional singleShapeBroadType(SemType t, SymbolTable symTable) { + if (PredefinedType.NIL.equals(t)) { + return Optional.of(symTable.nilType); + } else if (t instanceof BasicTypeBitSet) { + return Optional.empty(); + } else if (SemTypes.isSubtypeSimple(t, PredefinedType.INT)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_INT); + Optional value = IntSubtype.intSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(symTable.intType); + } else if (SemTypes.isSubtypeSimple(t, PredefinedType.FLOAT)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_FLOAT); + Optional value = FloatSubtype.floatSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(symTable.floatType); + } else if (SemTypes.isSubtypeSimple(t, PredefinedType.STRING)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_STRING); + Optional value = StringSubtype.stringSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(symTable.stringType); + } else if (SemTypes.isSubtypeSimple(t, PredefinedType.BOOLEAN)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_BOOLEAN); + Optional value = BooleanSubtype.booleanSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(symTable.booleanType); + } else if (SemTypes.isSubtypeSimple(t, PredefinedType.DECIMAL)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_DECIMAL); + Optional value = DecimalSubtype.decimalSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(symTable.decimalType); + } + return Optional.empty(); + } + + /** + * Returns the basic types of singleton/union of singleton. + *

+ * This will replace the existing finiteType.getValueSpace().iterator() calls + * + * @param t SemType component of BFiniteType + */ + public static Set broadTypes(SemType t, SymbolTable symTable) { // Equivalent to getValueTypes() + Set types = new LinkedHashSet<>(7); + BasicTypeBitSet basicTypeBitSet = widenToBasicTypes(t); + if ((basicTypeBitSet.bitset & PredefinedType.NIL.bitset) != 0) { + types.add(symTable.nilType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.BOOLEAN.bitset) != 0) { + types.add(symTable.booleanType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.INT.bitset) != 0) { + types.add(symTable.intType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.FLOAT.bitset) != 0) { + types.add(symTable.floatType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.DECIMAL.bitset) != 0) { + types.add(symTable.decimalType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.STRING.bitset) != 0) { + types.add(symTable.stringType); + } + + return types; + } + + public static Set broadTypes(BFiniteType finiteType, SymbolTable symTable) { + Set types = new LinkedHashSet<>(7); + for (SemNamedType semNamedType: finiteType.valueSpace) { + SemType t = semNamedType.semType(); + BasicTypeBitSet basicTypeBitSet = widenToBasicTypes(t); + if ((basicTypeBitSet.bitset & PredefinedType.NIL.bitset) != 0) { + types.add(symTable.nilType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.BOOLEAN.bitset) != 0) { + types.add(symTable.booleanType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.INT.bitset) != 0) { + types.add(symTable.intType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.FLOAT.bitset) != 0) { + types.add(symTable.floatType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.DECIMAL.bitset) != 0) { + types.add(symTable.decimalType); + } + + if ((basicTypeBitSet.bitset & PredefinedType.STRING.bitset) != 0) { + types.add(symTable.stringType); + } + } + return types; + } + + /** + * Counts number of bits set in bitset. + * Note: this is similar to lib:bitCount() in nBallerina + * This is the Brian Kernighan algorithm. + * This won't work if bits is less than 0. + * + * @param bitset bitset for bits to be counted + * @return the count + */ + public static int bitCount(int bitset) { + int n = 0; + int v = bitset; + while (v != 0) { + v &= v - 1; + n += 1; + } + return n; + } +} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java index 5b8566b99307..140bb9aa8aa0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SemanticAnalyzer.java @@ -20,6 +20,8 @@ import io.ballerina.compiler.api.symbols.DiagnosticState; import io.ballerina.projects.ModuleDescriptor; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; import org.ballerinalang.compiler.CompilerPhase; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.AttachPoint; @@ -270,6 +272,7 @@ public class SemanticAnalyzer extends SimpleBLangNodeAnalyzer anonTypeNameSuffixes; private final CompilerContext compilerContext; + private final Env typeEnv; public static SemanticAnalyzer getInstance(CompilerContext context) { SemanticAnalyzer semAnalyzer = context.get(SYMBOL_ANALYZER_KEY); @@ -297,6 +300,7 @@ private SemanticAnalyzer(CompilerContext context) { this.anonModelHelper = BLangAnonymousModelHelper.getInstance(context); this.unifier = new Unifier(); this.anonTypeNameSuffixes = new ArrayDeque<>(); + this.typeEnv = types.typeEnv(); } public BLangPackage analyze(BLangPackage pkgNode) { @@ -562,8 +566,8 @@ public void visit(BLangFunction funcNode, AnalyzerData data) { validateIsolatedParamUsage(inIsolatedFunction, restParam, true, data); } - if (hasReturnType && Symbols.isFlagOn(returnTypeNode.getBType().flags, Flags.PARAMETERIZED)) { - unifier.validate(returnTypeNode.getBType(), funcNode, symTable, currentEnv, types, dlog); + if (hasReturnType && Symbols.isFlagOn(returnTypeNode.getBType().getFlags(), Flags.PARAMETERIZED)) { + unifier.validate(typeEnv, returnTypeNode.getBType(), funcNode, symTable, currentEnv, types, dlog); } validateObjectAttachedFunction(funcNode, data); @@ -797,8 +801,6 @@ public void visit(BLangTypeConversionExpr conversionExpr, AnalyzerData data) { @Override public void visit(BLangFiniteTypeNode finiteTypeNode, AnalyzerData data) { - boolean foundUnaryExpr = false; - boolean isErroredExprInFiniteType = false; NodeKind valueKind; BLangExpression value; @@ -807,11 +809,6 @@ public void visit(BLangFiniteTypeNode finiteTypeNode, AnalyzerData data) { valueKind = value.getKind(); if (valueKind == NodeKind.UNARY_EXPR) { - foundUnaryExpr = true; - BType resultType = typeChecker.checkExpr(value, data.env, symTable.noType, data.prevEnvs); - if (resultType == symTable.semanticError) { - isErroredExprInFiniteType = true; - } // Replacing unary expression with numeric literal type for + and - numeric values BLangNumericLiteral newNumericLiteral = Types.constructNumericLiteralFromUnaryExpr((BLangUnaryExpr) value); @@ -824,10 +821,6 @@ public void visit(BLangFiniteTypeNode finiteTypeNode, AnalyzerData data) { analyzeNode(value, data); } } - - if (foundUnaryExpr && isErroredExprInFiniteType) { - finiteTypeNode.setBType(symTable.semanticError); - } } @Override @@ -953,7 +946,7 @@ public void visit(BLangRecordTypeNode recordTypeNode, AnalyzerData data) { if (isRecordType && allReadOnlyFields) { type.tsymbol.flags |= Flags.READONLY; - type.flags |= Flags.READONLY; + type.addFlags(Flags.READONLY); } validateDefaultable(recordTypeNode); @@ -1160,10 +1153,14 @@ public void visit(BLangSimpleVariable varNode, AnalyzerData data) { validateWorkerAnnAttachments(varNode.expr, data); + if (varNode.typeNode != null) { + analyzeNode(varNode.typeNode, data); + } handleWildCardBindingVariable(varNode, currentEnv); BType lhsType = varNode.symbol.type; varNode.setBType(lhsType); + // Configurable variable type must be a subtype of anydata. if (configurable && varNode.typeNode != null && lhsType.tag != TypeTags.SEMANTIC_ERROR) { if (!types.isAssignable(lhsType, symTable.anydataType)) { @@ -1182,10 +1179,6 @@ public void visit(BLangSimpleVariable varNode, AnalyzerData data) { } } - if (varNode.typeNode != null) { - analyzeNode(varNode.typeNode, data); - } - // Analyze the init expression BLangExpression rhsExpr = varNode.expr; if (rhsExpr == null) { @@ -1207,7 +1200,7 @@ public void visit(BLangSimpleVariable varNode, AnalyzerData data) { if (isListenerDecl) { BType rhsType = typeChecker.checkExpr(rhsExpr, varInitEnv, - BUnionType.create(null, lhsType, symTable.errorType), data.prevEnvs, + BUnionType.create(typeEnv, null, lhsType, symTable.errorType), data.prevEnvs, data.commonAnalyzerData); validateListenerCompatibility(varNode, rhsType); } else { @@ -1257,7 +1250,7 @@ private Map getModuleKeys(Set configVars, String } private void validateMapConfigVariable(String configKey, BVarSymbol variable, Map configKeys) { - if (configKeys.containsKey(configKey) && types.isSubTypeOfMapping(variable.type)) { + if (configKeys.containsKey(configKey) && types.isSubTypeOfMapping(variable.type.semType())) { dlog.error(variable.pos, DiagnosticErrorCode.CONFIGURABLE_VARIABLE_MODULE_AMBIGUITY, variable.name.value, configKeys.get(configKey)); } @@ -1347,7 +1340,7 @@ private boolean isSupportedConfigType(BType type, List errors, String va case ANYDATA: break; case FINITE: - return types.isAnydata(type); + return types.isAnydata(type.semType()); case NIL: return !isRequired; case ARRAY: @@ -1424,21 +1417,9 @@ private boolean isSupportedConfigType(BType type, List errors, String va } private boolean isNilableDefaultField(BField field, BType fieldType) { - if (!Symbols.isFlagOn(field.symbol.flags, Flags.REQUIRED) && !Symbols.isFlagOn(field.symbol.flags, - Flags.OPTIONAL)) { - if (fieldType.tag == TypeTags.NIL) { - return true; - } - if (fieldType.tag == TypeTags.UNION) { - BUnionType unionType = (BUnionType) fieldType; - for (BType memberType : unionType.getMemberTypes()) { - if (memberType.tag == TypeTags.NIL) { - return true; - } - } - } - } - return false; + return !Symbols.isFlagOn(field.symbol.flags, Flags.REQUIRED) && + !Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL) && + fieldType.isNullable(); } private void validateListenerCompatibility(BLangSimpleVariable varNode, BType rhsType) { @@ -1705,16 +1686,16 @@ private BType resolveTupleType(BLangTupleVariable varNode) { List members = new ArrayList<>(varNode.memberVariables.size()); for (BLangVariable memberVariable : varNode.memberVariables) { BType type = getTupleMemberType(memberVariable); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); members.add(new BTupleMember(type, varSymbol)); } BLangVariable restVariable = varNode.restVariable; if (restVariable == null) { - return new BTupleType(members); + return new BTupleType(typeEnv, members); } - return new BTupleType(null, members, getTupleMemberType(restVariable), 0); + return new BTupleType(typeEnv, null, members, getTupleMemberType(restVariable), 0); } private BType getTupleMemberType(BLangVariable memberVariable) { @@ -1751,7 +1732,7 @@ public void visit(BLangErrorVariable varNode, AnalyzerData data) { // reason must be a const of subtype of string. // then we match the error with this specific reason. if (!varNode.reasonVarPrefixAvailable && varNode.getBType() == null) { - BErrorType errorType = new BErrorType(varNode.getBType().tsymbol, null); + BErrorType errorType = new BErrorType(typeEnv, varNode.getBType().tsymbol, null); if (Types.getImpliedType(varNode.getBType()).tag == TypeTags.UNION) { Set members = types.expandAndGetMemberTypesRecursive(varNode.getBType()); @@ -2030,7 +2011,7 @@ private BType getListenerType(BType bType) { } else if (compatibleTypes.size() == 1) { return compatibleTypes.iterator().next(); } else { - return BUnionType.create(null, compatibleTypes); + return BUnionType.create(typeEnv, null, compatibleTypes); } } @@ -2077,7 +2058,7 @@ void handleDeclaredVarInForeach(BLangVariable variable, BType rhsType, SymbolEnv BLangTupleVariable tupleVariable = (BLangTupleVariable) variable; if ((TypeTags.TUPLE != referredRhsType.tag && TypeTags.ARRAY != referredRhsType.tag && TypeTags.UNION != referredRhsType.tag) || - (variable.isDeclaredWithVar && !types.isSubTypeOfBaseType(rhsType, TypeTags.TUPLE))) { + (variable.isDeclaredWithVar && !types.isSubTypeOfBaseType(rhsType, PredefinedType.LIST))) { dlog.error(variable.pos, DiagnosticErrorCode.INVALID_LIST_BINDING_PATTERN_INFERENCE, rhsType); recursivelyDefineVariables(tupleVariable, blockEnv); return; @@ -2529,7 +2510,7 @@ private void checkArrayVarRefEquivalency(Location pos, BLangTupleVarRef target, BArrayType arraySource = (BArrayType) source; // For unsealed - if (arraySource.size < target.expressions.size() && arraySource.state != BArrayState.OPEN) { + if (arraySource.getSize() < target.expressions.size() && arraySource.state != BArrayState.OPEN) { dlog.error(rhsPos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, target.getBType(), arraySource); } @@ -2699,7 +2680,7 @@ private void checkErrorVarRefEquivalency(BLangErrorVarRef lhsRef, BType rhsType, if (lhsRef.restVar != null && !isIgnoreVar(lhsRef)) { setTypeOfVarRefInErrorBindingAssignment(lhsRef.restVar, data); checkInvalidTypeDef(lhsRef.restVar); - BMapType expRestType = new BMapType(TypeTags.MAP, wideType, null); + BMapType expRestType = new BMapType(typeEnv, TypeTags.MAP, wideType, null); BType restVarType = Types.getImpliedType(lhsRef.restVar.getBType()); if (restVarType.tag != TypeTags.MAP || !types.isAssignable(wideType, ((BMapType) restVarType).constraint)) { dlog.error(lhsRef.restVar.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, lhsRef.restVar.getBType(), @@ -2714,7 +2695,7 @@ private void checkErrorVarRefEquivalency(BLangErrorVarRef lhsRef, BType rhsType, private BType interpolateWideType(BRecordType rhsDetailType, List detailType) { Set extractedKeys = detailType.stream().map(detail -> detail.name.value).collect(Collectors.toSet()); - BUnionType wideType = BUnionType.create(null); + BUnionType wideType = BUnionType.create(typeEnv, null); for (BField field : rhsDetailType.fields.values()) { // avoid fields extracted from binding pattern if (!extractedKeys.contains(field.name.value)) { @@ -2835,7 +2816,8 @@ public void visit(BLangIf ifNode, AnalyzerData data) { if (existingNarrowedTypeInfo.containsKey(key)) { BType.NarrowedTypes existingNarrowTypes = existingNarrowedTypeInfo.get(key); BUnionType unionType = - BUnionType.create(null, existingNarrowTypes.trueType, existingNarrowTypes.falseType); + BUnionType.create(typeEnv, null, existingNarrowTypes.trueType, + existingNarrowTypes.falseType); BType.NarrowedTypes newPair = new BType.NarrowedTypes(existingNarrowTypes.trueType, unionType); falseTypesOfNarrowedTypes.put(key, newPair); } @@ -3005,10 +2987,10 @@ private void evaluateMatchPatternsTypeAccordingToMatchGuard(BLangMatchPattern ma for (BLangMatchPattern memberMatchPattern : listMatchPattern.matchPatterns) { evaluateMatchPatternsTypeAccordingToMatchGuard(memberMatchPattern, env); BType type = memberMatchPattern.getBType(); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); members.add(new BTupleMember(type, varSymbol)); } - BTupleType matchPatternType = new BTupleType(members); + BTupleType matchPatternType = new BTupleType(typeEnv, members); if (listMatchPattern.restMatchPattern != null) { evaluateMatchPatternsTypeAccordingToMatchGuard(listMatchPattern.restMatchPattern, env); @@ -3059,7 +3041,7 @@ public void visit(BLangMappingMatchPattern mappingMatchPattern, AnalyzerData dat fields.put(fieldName.getValue(), field); mappingMatchPattern.declaredVars.putAll(fieldMatchPattern.declaredVars); } - BRecordType recordVarType = new BRecordType(recordSymbol); + BRecordType recordVarType = new BRecordType(typeEnv, recordSymbol); recordVarType.fields = fields; recordVarType.restFieldType = symTable.anyOrErrorType; if (mappingMatchPattern.restMatchPattern != null) { @@ -3067,7 +3049,7 @@ public void visit(BLangMappingMatchPattern mappingMatchPattern, AnalyzerData dat symbolEnter.createAnonRecordSymbol(currentEnv, mappingMatchPattern.pos); BLangRestMatchPattern restMatchPattern = mappingMatchPattern.restMatchPattern; BType restType = restMatchPattern.getBType(); - BRecordType matchPatternRecType = new BRecordType(matchPattenRecordSym); + BRecordType matchPatternRecType = new BRecordType(typeEnv, matchPattenRecordSym); matchPatternRecType.restFieldType = restType != null ? restType : symTable.anyOrErrorType; recordVarType.restFieldType = matchPatternRecType.restFieldType; restMatchPattern.setBType(matchPatternRecType); @@ -3119,7 +3101,7 @@ private void assignTypesToMemberPatterns(BLangMatchPattern matchPattern, BType b BType type = arrayType.eType; BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); BTupleType restTupleType = createTupleForClosedArray( - arrayType.size - listMatchPattern.matchPatterns.size(), + arrayType.getSize() - listMatchPattern.matchPatterns.size(), new BTupleMember(type, varSymbol)); listMatchPattern.restMatchPattern.setBType(restTupleType); BVarSymbol restMatchPatternSymbol = listMatchPattern.restMatchPattern.declaredVars @@ -3143,11 +3125,11 @@ private void assignTypesToMemberPatterns(BLangMatchPattern matchPattern, BType b for (int i = 0; i < matchPatterns.size(); i++) { assignTypesToMemberPatterns(matchPatterns.get(i), members.get(i).type, data); BType type = matchPatterns.get(i).getBType(); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); newMembers.add(new BTupleMember(type, varSymbol)); } - BTupleType tupleType = new BTupleType(newMembers); + BTupleType tupleType = new BTupleType(typeEnv, newMembers); if (listMatchPattern.restMatchPattern == null) { listMatchPattern.setBType(tupleType); @@ -3200,7 +3182,7 @@ private void assignTypesToMemberPatterns(BLangMatchPattern matchPattern, BType b private BTupleType createTupleForClosedArray(int noOfElements, BTupleMember elementType) { List members = Collections.nCopies(noOfElements, elementType); - return new BTupleType(members); + return new BTupleType(typeEnv, members); } private BType createTypeForTupleRestType(int startIndex, List members, BType patternRestType) { @@ -3209,16 +3191,16 @@ private BType createTypeForTupleRestType(int startIndex, List memb remainingMembers.add(members.get(i)); } if (!remainingMembers.isEmpty()) { - BTupleType restTupleType = new BTupleType(remainingMembers); + BTupleType restTupleType = new BTupleType(typeEnv, remainingMembers); if (patternRestType != null) { restTupleType.restType = patternRestType; } return restTupleType; } else { if (patternRestType != null) { - return new BArrayType(patternRestType); + return new BArrayType(typeEnv, patternRestType); } else { - return new BArrayType(symTable.anyOrErrorType); + return new BArrayType(typeEnv, symTable.anyOrErrorType); } } } @@ -3318,18 +3300,18 @@ public void visit(BLangListBindingPattern listBindingPattern, AnalyzerData data) for (BLangBindingPattern bindingPattern : listBindingPattern.bindingPatterns) { analyzeNode(bindingPattern, data); BType type = bindingPattern.getBType(); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); listMembers.add(new BTupleMember(type, varSymbol)); listBindingPattern.declaredVars.putAll(bindingPattern.declaredVars); } - BTupleType listBindingPatternType = new BTupleType(listMembers); + BTupleType listBindingPatternType = new BTupleType(typeEnv, listMembers); if (listBindingPattern.restBindingPattern != null) { BLangRestBindingPattern restBindingPattern = listBindingPattern.restBindingPattern; BType restBindingPatternType = restBindingPattern.getBType(); BType restType = restBindingPatternType != null ? restBindingPatternType : symTable.anyOrErrorType; - restBindingPattern.setBType(new BArrayType(restType)); + restBindingPattern.setBType(new BArrayType(typeEnv, restType)); restBindingPattern.accept(this, data); listBindingPattern.declaredVars.put(restBindingPattern.variableName.value, restBindingPattern.symbol); listBindingPatternType.restType = restType; @@ -3426,7 +3408,7 @@ public void visit(BLangErrorFieldBindingPatterns errorFieldBindingPatterns, Anal } if (errorFieldBindingPatterns.restBindingPattern != null) { errorFieldBindingPatterns.restBindingPattern.setBType( - new BMapType(TypeTags.MAP, symTable.anydataType, null)); + new BMapType(typeEnv, TypeTags.MAP, symTable.anydataType, null)); analyzeNode(errorFieldBindingPatterns.restBindingPattern, data); errorFieldBindingPatterns.declaredVars.putAll(errorFieldBindingPatterns.restBindingPattern.declaredVars); } @@ -3540,7 +3522,8 @@ public void visit(BLangErrorFieldMatchPatterns errorFieldMatchPatterns, Analyzer errorFieldMatchPatterns.declaredVars.putAll(namedArgMatchPattern.declaredVars); } if (errorFieldMatchPatterns.restMatchPattern != null) { - errorFieldMatchPatterns.restMatchPattern.setBType(new BMapType(TypeTags.MAP, symTable.anydataType, null)); + errorFieldMatchPatterns.restMatchPattern.setBType(new BMapType(typeEnv, TypeTags.MAP, + symTable.anydataType, null)); analyzeNode(errorFieldMatchPatterns.restMatchPattern, data); errorFieldMatchPatterns.declaredVars.putAll(errorFieldMatchPatterns.restMatchPattern.declaredVars); } @@ -3595,7 +3578,7 @@ public void visit(BLangMappingBindingPattern mappingBindingPattern, AnalyzerData fields.put(fieldName.getValue(), field); mappingBindingPattern.declaredVars.putAll(fieldBindingPattern.declaredVars); } - BRecordType recordVarType = new BRecordType(recordSymbol); + BRecordType recordVarType = new BRecordType(typeEnv, recordSymbol); recordVarType.fields = fields; recordVarType.restFieldType = symTable.anyOrErrorType; if (mappingBindingPattern.restBindingPattern != null) { @@ -3603,7 +3586,7 @@ public void visit(BLangMappingBindingPattern mappingBindingPattern, AnalyzerData BType restType = restBindingPattern.getBType(); BRecordTypeSymbol matchPattenRecordSym = symbolEnter.createAnonRecordSymbol(currentEnv, restBindingPattern.pos); - BRecordType matchPatternRecType = new BRecordType(matchPattenRecordSym); + BRecordType matchPatternRecType = new BRecordType(typeEnv, matchPattenRecordSym); matchPatternRecType.restFieldType = restType != null ? restType : symTable.anyOrErrorType; recordVarType.restFieldType = matchPatternRecType.restFieldType; restBindingPattern.setBType(matchPatternRecType); @@ -3657,18 +3640,18 @@ public void visit(BLangListMatchPattern listMatchPattern, AnalyzerData data) { for (BLangMatchPattern memberMatchPattern : listMatchPattern.matchPatterns) { memberMatchPattern.accept(this, data); BType type = memberMatchPattern.getBType(); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); members.add(new BTupleMember(type, varSymbol)); checkForSimilarVars(listMatchPattern.declaredVars, memberMatchPattern.declaredVars, memberMatchPattern.pos); listMatchPattern.declaredVars.putAll(memberMatchPattern.declaredVars); } - BTupleType matchPatternType = new BTupleType(members); + BTupleType matchPatternType = new BTupleType(typeEnv, members); if (listMatchPattern.getRestMatchPattern() != null) { BLangRestMatchPattern restMatchPattern = (BLangRestMatchPattern) listMatchPattern.getRestMatchPattern(); BType restBindingPatternType = restMatchPattern.getBType(); BType restType = restBindingPatternType != null ? restBindingPatternType : symTable.anyOrErrorType; - restMatchPattern.setBType(new BArrayType(restType)); + restMatchPattern.setBType(new BArrayType(typeEnv, restType)); restMatchPattern.accept(this, data); checkForSimilarVars(listMatchPattern.declaredVars, restMatchPattern.declaredVars, restMatchPattern.pos); listMatchPattern.declaredVars.put(restMatchPattern.variableName.value, restMatchPattern.symbol); @@ -3725,7 +3708,7 @@ private void assignTypesToMemberPatterns(BLangBindingPattern bindingPattern, BTy BType type = arrayType.eType; BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); BTupleType restTupleType = createTupleForClosedArray( - arrayType.size - listBindingPattern.bindingPatterns.size(), + arrayType.getSize() - listBindingPattern.bindingPatterns.size(), new BTupleMember(type, varSymbol)); listBindingPattern.restBindingPattern.setBType(restTupleType); BVarSymbol restBindingPatternSymbol = listBindingPattern.restBindingPattern.declaredVars @@ -3749,10 +3732,10 @@ private void assignTypesToMemberPatterns(BLangBindingPattern bindingPattern, BTy for (int i = 0; i < bindingPatterns.size(); i++) { assignTypesToMemberPatterns(bindingPatterns.get(i), tupleMemebers.get(i).type, data); BType type = bindingPatterns.get(i).getBType(); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); members.add(new BTupleMember(type, varSymbol)); } - BTupleType tupleType = new BTupleType(members); + BTupleType tupleType = new BTupleType(typeEnv, members); if (listBindingPattern.restBindingPattern == null) { bindingPattern.setBType(tupleType); @@ -3878,7 +3861,7 @@ public void visit(BLangOnFailClause onFailClause, AnalyzerData data) { if (currentOnFailErrTypes.size() == 1) { failErrorType = currentOnFailErrTypes.iterator().next(); } else if (currentOnFailErrTypes.size() > 1) { - failErrorType = BUnionType.create(null, currentOnFailErrTypes); + failErrorType = BUnionType.create(typeEnv, null, currentOnFailErrTypes); } else { failErrorType = symTable.neverType; } @@ -3992,7 +3975,7 @@ public void visit(BLangFail failNode, AnalyzerData data) { } } if (errorExpressionType != symTable.semanticError && - !types.isSubTypeOfBaseType(errorExpressionType, symTable.errorType.tag)) { + !types.isSubTypeOfBaseType(errorExpressionType, PredefinedType.ERROR)) { dlog.error(errorExpression.pos, DiagnosticErrorCode.ERROR_TYPE_EXPECTED, errorExpressionType); } data.notCompletedNormally = true; @@ -4032,8 +4015,6 @@ public void visit(BLangService serviceNode, AnalyzerData data) { dlog.error(attachExpr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, LISTENER_NAME, exprType); } else if (exprType != symTable.semanticError && serviceNode.listenerType == null) { serviceNode.listenerType = exprType; - } else if (exprType != symTable.semanticError) { - this.types.isSameType(exprType, serviceNode.listenerType); } if (attachExpr.getKind() == NodeKind.SIMPLE_VARIABLE_REF) { @@ -4100,7 +4081,7 @@ private void inferServiceTypeFromListeners(BLangService serviceNode, AnalyzerDat for (BType attachType : listenerTypes) { typeIdSet.add(getTypeIds(attachType)); } - inferred = BUnionType.create(null, listenerTypes); + inferred = BUnionType.create(typeEnv, null, listenerTypes); } serviceNode.inferredServiceType = inferred; @@ -4223,7 +4204,7 @@ public void visit(BLangTransaction transactionNode, AnalyzerData data) { @Override public void visit(BLangRollback rollbackNode, AnalyzerData data) { if (rollbackNode.expr != null) { - BType expectedType = BUnionType.create(null, symTable.errorType, symTable.nilType); + BType expectedType = BUnionType.create(typeEnv, null, symTable.errorType, symTable.nilType); this.typeChecker.checkExpr(rollbackNode.expr, data.env, expectedType, data.prevEnvs, data.commonAnalyzerData); } @@ -4788,7 +4769,7 @@ private void validateInclusions(Set referencingTypeFlags, List for (BLangType typeRef : typeRefs) { BType type = typeRef.getBType(); - long flags = type.flags; + long flags = type.getFlags(); List mismatchedFlags = new ArrayList<>(); @@ -4941,7 +4922,7 @@ private void validateIsolatedParamUsage(boolean inIsolatedFunction, BLangSimpleV BType type = isRestParam ? ((BArrayType) variable.getBType()).eType : variable.getBType(); - if (!types.isSubTypeOfBaseType(type, TypeTags.INVOKABLE)) { + if (!types.isSubTypeOfBaseType(type, PredefinedType.FUNCTION)) { dlog.error(variable.pos, DiagnosticErrorCode.ISOLATED_PARAM_USED_WITH_INVALID_TYPE); } @@ -4990,7 +4971,7 @@ private void handleReadOnlyField(boolean isRecordType, LinkedHashMap unresolvedTypes; private Set unresolvedRecordDueToFields; private boolean resolveRecordsUnresolvedDueToFields; - private List unresolvedClasses; private final HashSet unknownTypeRefs; private final List importedPackages; private int typePrecedence; @@ -241,7 +242,6 @@ public class SymbolEnter extends BLangNodeVisitor { private final BLangMissingNodesHelper missingNodesHelper; private final PackageCache packageCache; private final List intersectionTypes; - private Map typeToTypeDef; private SymbolEnv env; private final boolean projectAPIInitiatedCompilation; @@ -274,6 +274,7 @@ public SymbolEnter(CompilerContext context) { this.unknownTypeRefs = new HashSet<>(); this.missingNodesHelper = BLangMissingNodesHelper.getInstance(context); this.packageCache = PackageCache.getInstance(context); + this.constResolver = ConstantValueResolver.getInstance(context); this.intersectionTypes = new ArrayList<>(); CompilerOptions options = CompilerOptions.getInstance(context); @@ -491,20 +492,6 @@ private void defineConstructs(BLangPackage pkgNode, SymbolEnv pkgEnv) { typeResolver.clearUnknowTypeRefs(); } - private void defineDependentFields(List typeDefNodes, SymbolEnv pkgEnv) { - for (BLangNode typeDef : typeDefNodes) { - if (typeDef.getKind() == NodeKind.CLASS_DEFN) { - BLangClassDefinition classDefinition = (BLangClassDefinition) typeDef; - if (isObjectCtor(classDefinition)) { - continue; - } - defineReferencedFieldsOfClassDef(classDefinition, pkgEnv); - } else if (typeDef.getKind() == NodeKind.TYPE_DEFINITION) { - defineReferencedFieldsOfRecordTypeDef((BLangTypeDefinition) typeDef); - } - } - } - public void defineReferencedFieldsOfClassDef(BLangClassDefinition classDefinition, SymbolEnv pkgEnv) { SymbolEnv typeDefEnv = classDefinition.typeDefEnv; BObjectTypeSymbol tSymbol = (BObjectTypeSymbol) classDefinition.symbol; @@ -513,13 +500,6 @@ public void defineReferencedFieldsOfClassDef(BLangClassDefinition classDefinitio defineReferencedClassFields(classDefinition, typeDefEnv, objType, false); } - private void defineIntersectionTypes(SymbolEnv env) { - for (BLangNode typeDescriptor : this.intersectionTypes) { - defineNode(typeDescriptor, env); - } - this.intersectionTypes.clear(); - } - private void defineErrorType(Location pos, BErrorType errorType, SymbolEnv env) { SymbolEnv pkgEnv = symTable.pkgEnvMap.get(env.enclPkg.symbol); BTypeSymbol errorTSymbol = errorType.tsymbol; @@ -528,18 +508,6 @@ private void defineErrorType(Location pos, BErrorType errorType, SymbolEnv env) if (symResolver.checkForUniqueSymbol(pos, pkgEnv, errorTSymbol)) { pkgEnv.scope.define(errorTSymbol.name, errorTSymbol); } - - SymbolEnv prevEnv = this.env; - this.env = pkgEnv; - this.env = prevEnv; - } - - private boolean isObjectCtor(BLangNode node) { - if (node.getKind() == NodeKind.CLASS_DEFN) { - BLangClassDefinition classDefinition = (BLangClassDefinition) node; - return isObjectCtor(classDefinition); - } - return false; } public boolean isObjectCtor(BLangClassDefinition classDefinition) { @@ -893,7 +861,7 @@ public void visit(BLangClassDefinition classDefinition) { typeFlags |= Flags.OBJECT_CTOR; } - BObjectType objectType = new BObjectType(tSymbol, typeFlags); + BObjectType objectType = new BObjectType(symTable.typeEnv(), tSymbol, typeFlags); if (classDefinition.isObjectContructorDecl || flags.contains(Flag.OBJECT_CTOR)) { classDefinition.oceEnvData.objectType = objectType; objectType.classDef = classDefinition; @@ -904,7 +872,7 @@ public void visit(BLangClassDefinition classDefinition) { } if (flags.contains(Flag.CLIENT)) { - objectType.flags |= Flags.CLIENT; + objectType.addFlags(Flags.CLIENT); } tSymbol.type = objectType; @@ -1233,83 +1201,6 @@ public void visit(BLangXMLNSStatement xmlnsStmtNode) { defineNode(xmlnsStmtNode.xmlnsDecl, env); } - private void defineTypeNodes(List typeDefs, SymbolEnv env) { - if (typeDefs.isEmpty()) { - return; - } - - this.unresolvedTypes = new ArrayList<>(typeDefs.size()); - this.unresolvedRecordDueToFields = new HashSet<>(typeDefs.size()); - this.resolveRecordsUnresolvedDueToFields = false; - for (BLangNode typeDef : typeDefs) { - if (isErrorIntersectionTypeCreatingNewType(typeDef, env)) { - populateUndefinedErrorIntersection((BLangTypeDefinition) typeDef, env); - continue; - } -// if (isObjectCtor(typeDef)) { -// continue; -// } - - defineNode(typeDef, env); - } - - if (typeDefs.size() <= unresolvedTypes.size()) { - - this.resolveRecordsUnresolvedDueToFields = true; - unresolvedTypes.removeAll(unresolvedRecordDueToFields); - for (BLangNode unresolvedType : unresolvedRecordDueToFields) { - defineNode(unresolvedType, env); - } - this.resolveRecordsUnresolvedDueToFields = false; - - // This situation can occur due to either a cyclic dependency or at least one of member types in type - // definition node cannot be resolved. So we iterate through each node recursively looking for cyclic - // dependencies or undefined types in type node. - - for (BLangNode unresolvedType : unresolvedTypes) { - Deque references = new ArrayDeque<>(); - NodeKind unresolvedKind = unresolvedType.getKind(); - if (unresolvedKind == NodeKind.TYPE_DEFINITION || unresolvedKind == NodeKind.CONSTANT) { - TypeDefinition def = (TypeDefinition) unresolvedType; - // We need to keep track of all visited types to print cyclic dependency. - references.push(def.getName().getValue()); - checkErrors(env, unresolvedType, (BLangNode) def.getTypeNode(), references, false); - } else if (unresolvedType.getKind() == NodeKind.CLASS_DEFN) { - BLangClassDefinition classDefinition = (BLangClassDefinition) unresolvedType; - references.push(classDefinition.getName().getValue()); - checkErrors(env, unresolvedType, classDefinition, references, true); - } - } - defineAllUnresolvedCyclicTypesInScope(env); - - Set alreadyDefinedTypeDefNames = new HashSet<>(); - int unresolvedTypeCount = unresolvedTypes.size(); - for (int i = 0; i < unresolvedTypeCount; i++) { - for (BLangNode node : this.unresolvedTypes) { - String name = getTypeOrClassName(node); - boolean symbolNotFound = false; - boolean isTypeOrClassDefinition = - node.getKind() == NodeKind.TYPE_DEFINITION || node.getKind() == NodeKind.CLASS_DEFN; - // Skip the type resolving in the first iteration (i == 0) - // as we want to define the type before trying to resolve it. - if (isTypeOrClassDefinition && i != 0) { // Do not skip the first iteration - BSymbol bSymbol = symResolver.lookupSymbolInMainSpace(env, Names.fromString(name)); - symbolNotFound = (bSymbol == symTable.notFoundSymbol); - } - - boolean notFoundInList = alreadyDefinedTypeDefNames.add(name); - - // Prevent defining already defined type names. - if (notFoundInList || symbolNotFound) { - defineNode(node, env); - } - } - } - return; - } - defineTypeNodes(unresolvedTypes, env); - } - private void populateUndefinedErrorIntersection(BLangTypeDefinition typeDef, SymbolEnv env) { long flags = 0; if (typeDef.flagSet.contains(Flag.PUBLIC)) { @@ -1557,7 +1448,7 @@ private void checkErrorsOfUserDefinedType(SymbolEnv env, BLangNode unresolvedTyp } } - public String getTypeOrClassName(BLangNode node) { + public static String getTypeOrClassName(BLangNode node) { if (node.getKind() == NodeKind.TYPE_DEFINITION || node.getKind() == NodeKind.CONSTANT) { return ((TypeDefinition) node).getName().getValue(); } else { @@ -1657,7 +1548,7 @@ public void visit(BLangTypeDefinition typeDefinition) { typeDefSymbol.pkgID, typeDefSymbol.type, typeDefSymbol.owner, typeDefSymbol.pos, typeDefSymbol.origin); typeSymbol.markdownDocumentation = typeDefSymbol.markdownDocumentation; ((BTypeDefinitionSymbol) typeDefSymbol).referenceType = new BTypeReferenceType(definedType, typeSymbol, - typeDefSymbol.type.flags); + typeDefSymbol.type.getFlags()); boolean isLabel = true; //todo remove after type ref introduced to runtime @@ -1718,7 +1609,7 @@ public void visit(BLangTypeDefinition typeDefinition) { dlog.error(typeDefinition.pos, DiagnosticErrorCode.TYPE_PARAM_OUTSIDE_LANG_MODULE); } } - definedType.flags |= typeDefSymbol.flags; + definedType.addFlags(typeDefSymbol.flags); typeDefinition.symbol = typeDefSymbol; if (typeDefinition.hasCyclicReference) { @@ -1755,7 +1646,7 @@ public void handleDistinctDefinition(BLangTypeDefinition typeDefinition, BSymbol if (((BTypeDefinitionSymbol) typeDefSymbol).referenceType != null) { ((BTypeDefinitionSymbol) typeDefSymbol).referenceType.referredType = distinctType; } - definedType.flags |= Flags.DISTINCT; + definedType.addFlags(Flags.DISTINCT); } } @@ -1818,12 +1709,12 @@ public void populateAllReadyDefinedErrorIntersection(BType definedType, BLangTyp alreadyDefinedErrorType.typeIdSet = errorType.typeIdSet; alreadyDefinedErrorType.detailType = errorType.detailType; - alreadyDefinedErrorType.flags = errorType.flags; + alreadyDefinedErrorType.setFlags(errorType.getFlags()); alreadyDefinedErrorType.name = errorType.name; intersectionType.effectiveType = alreadyDefinedErrorType; if (!errorType.typeIdSet.isEmpty()) { - definedType.flags |= Flags.DISTINCT; + definedType.addFlags(Flags.DISTINCT); } } @@ -1861,7 +1752,7 @@ private BObjectType getDistinctObjectType(BLangTypeDefinition typeDefinition, BO // `typeDefSymbol` is different to `definedObjType.tsymbol` in a type definition statement that use // already defined type as the base type. if (definedObjType.tsymbol != tSymbol) { - BObjectType objType = new BObjectType(tSymbol); + BObjectType objType = new BObjectType(symTable.typeEnv(), tSymbol); tSymbol.type = objType; definedObjType = objType; } @@ -1886,13 +1777,13 @@ private BType defineSymbolForCyclicTypeDefinition(BLangTypeDefinition typeDef, S typeDefSymbol = switch (typeDef.typeNode.getKind()) { case TUPLE_TYPE_NODE -> { - newTypeNode = new BTupleType(null, new ArrayList<>(), true); + newTypeNode = new BTupleType(symTable.typeEnv(), null, new ArrayList<>(), true); yield Symbols.createTypeSymbol(SymTag.TUPLE_TYPE, Flags.asMask(typeDef.flagSet), newTypeDefName, env.enclPkg.symbol.pkgID, newTypeNode, env.scope.owner, typeDef.name.pos, SOURCE); } default -> { - newTypeNode = BUnionType.create(null, new LinkedHashSet<>(), true); + newTypeNode = BUnionType.create(symTable.typeEnv(), null, new LinkedHashSet<>(), true); yield Symbols.createTypeSymbol(SymTag.UNION_TYPE, Flags.asMask(typeDef.flagSet), newTypeDefName, env.enclPkg.symbol.pkgID, newTypeNode, env.scope.owner, typeDef.name.pos, SOURCE); @@ -1901,7 +1792,7 @@ private BType defineSymbolForCyclicTypeDefinition(BLangTypeDefinition typeDef, S typeDef.symbol = typeDefSymbol; defineTypeInMainScope(typeDefSymbol, typeDef, env); newTypeNode.tsymbol = typeDefSymbol; - newTypeNode.flags |= typeDefSymbol.flags; + newTypeNode.addFlags(typeDefSymbol.flags); return newTypeNode; } @@ -1976,7 +1867,7 @@ private BErrorType getDistinctErrorType(BLangTypeDefinition typeDefinition, BErr if (definedErrorType.tsymbol != typeDefSymbol) { BTypeSymbol typeSymbol = new BTypeSymbol(SymTag.TYPE_DEF, typeDefSymbol.flags, typeDefSymbol.name, typeDefSymbol.pkgID, null, typeDefSymbol.owner, typeDefSymbol.pos, typeDefSymbol.origin); - BErrorType bErrorType = new BErrorType(typeSymbol); + BErrorType bErrorType = new BErrorType(symTable.typeEnv(), typeSymbol); typeSymbol.type = bErrorType; bErrorType.detailType = definedErrorType.detailType; typeDefSymbol.type = bErrorType; @@ -2484,8 +2375,8 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t } if (memberTypes.size() > 1) { - BType type = BUnionType.create(null, memberTypes); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, + BType type = BUnionType.create(symTable.typeEnv(), null, memberTypes); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); members.add(new BTupleMember(type, varSymbol)); } else { @@ -2494,7 +2385,7 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t Symbols.createVarSymbolForTupleMember(m)))); } } - tupleTypeNode = new BTupleType(members); + tupleTypeNode = new BTupleType(symTable.typeEnv(), members); tupleTypeNode.restType = getPossibleRestTypeForUnion(varNode, possibleTypes); break; } @@ -2512,7 +2403,7 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); members.add(new BTupleMember(type, varSymbol)); } - tupleTypeNode = new BTupleType(members); + tupleTypeNode = new BTupleType(symTable.typeEnv(), members); tupleTypeNode.restType = getPossibleRestTypeForUnion(varNode, possibleTypes); break; case TypeTags.ANY: @@ -2522,7 +2413,7 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(referredType); memberTupleTypes.add(new BTupleMember(referredType, varSymbol)); } - tupleTypeNode = new BTupleType(memberTupleTypes); + tupleTypeNode = new BTupleType(symTable.typeEnv(), memberTupleTypes); if (varNode.restVariable != null) { tupleTypeNode.restType = referredType; } @@ -2533,9 +2424,9 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t case TypeTags.ARRAY: List tupleTypes = new ArrayList<>(); BArrayType arrayType = (BArrayType) referredType; - tupleTypeNode = new BTupleType(tupleTypes); + tupleTypeNode = new BTupleType(symTable.typeEnv(), tupleTypes); BType eType = arrayType.eType; - for (int i = 0; i < arrayType.size; i++) { + for (int i = 0; i < arrayType.getSize(); i++) { BType type = arrayType.eType; BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); tupleTypes.add(new BTupleMember(type, varSymbol)); @@ -2591,11 +2482,11 @@ boolean checkTypeAndVarCountConsistency(BLangTupleVariable varNode, BTupleType t } } if (!members.isEmpty()) { - BTupleType restTupleType = new BTupleType(members); + BTupleType restTupleType = new BTupleType(symTable.typeEnv(), members); restTupleType.restType = restType; type = restTupleType; } else { - type = restType != null ? new BArrayType(restType) : null; + type = restType != null ? new BArrayType(symTable.typeEnv(), restType) : null; } defineMemberNode(varNode.restVariable, env, type); } @@ -2632,7 +2523,7 @@ private BType getPossibleRestTypeForUnion(BLangTupleVariable varNode, List 1 ? BUnionType.create(null, memberRestTypes) : + return memberRestTypes.size() > 1 ? BUnionType.create(symTable.typeEnv(), null, memberRestTypes) : memberRestTypes.iterator().next(); } else { return varNode.getBType(); @@ -2797,7 +2688,7 @@ private BType createRestFieldFromPossibleTypes(Location pos, SymbolEnv env, List } BType restFieldType = restFieldMemberTypes.size() > 1 ? - BUnionType.create(null, restFieldMemberTypes) : + BUnionType.create(symTable.typeEnv(), null, restFieldMemberTypes) : restFieldMemberTypes.iterator().next(); if (!possibleRecordFieldMapList.isEmpty()) { @@ -2817,7 +2708,7 @@ private BType createRestFieldFromPossibleTypes(Location pos, SymbolEnv env, List } unmappedMembers.putAll(optionalFields); - BRecordType restRecord = new BRecordType(null); + BRecordType restRecord = new BRecordType(symTable.typeEnv(), null); restRecord.fields = unmappedMembers; restRecord.restFieldType = restFieldType; restFieldType = restRecord; @@ -2885,7 +2776,7 @@ private LinkedHashMap populateAndGetPossibleFieldsForRecVar(Loca } BType fieldType = memberTypes.size() > 1 ? - BUnionType.create(null, memberTypes) : memberTypes.iterator().next(); + BUnionType.create(symTable.typeEnv(), null, memberTypes) : memberTypes.iterator().next(); BField field = new BField(Names.fromString(fieldName), pos, new BVarSymbol(0, Names.fromString(fieldName), env.enclPkg.symbol.pkgID, fieldType, recordSymbol, pos, SOURCE)); @@ -2900,7 +2791,7 @@ private BRecordType createSameTypedFieldsRecordType(BLangRecordVariable recordVa if (fieldTypes.isNullable()) { fieldType = fieldTypes; } else { - fieldType = BUnionType.create(null, fieldTypes, symTable.nilType); + fieldType = BUnionType.create(symTable.typeEnv(), null, fieldTypes, symTable.nilType); } BRecordTypeSymbol recordSymbol = Symbols.createRecordSymbol(Flags.ANONYMOUS, @@ -3037,21 +2928,31 @@ public BRecordTypeSymbol createAnonRecordSymbol(SymbolEnv env, Location pos) { return recordSymbol; } - BType getRestParamType(BRecordType recordType) { + BType getRestParamType(BRecordType recordType) { BType memberType; if (recordType.restFieldType != null) { memberType = recordType.restFieldType; - } else if (hasErrorTypedField(recordType)) { - memberType = hasOnlyPureTypedFields(recordType) ? symTable.pureType : - BUnionType.create(null, symTable.anyType, symTable.errorType); - } else { - memberType = hasOnlyAnyDataTypedFields(recordType) ? symTable.anydataType : symTable.anyType; + BType referredMemberType = Types.getImpliedType(memberType); + if (referredMemberType.tag == TypeTags.RECORD) { + return getRestParamType((BRecordType) referredMemberType); + } else { + return memberType; + } } - BType referredMemberType = Types.getImpliedType(memberType); - if (referredMemberType.tag == TypeTags.RECORD) { - memberType = getRestParamType((BRecordType) referredMemberType); + + SemType s = recordType.semType(); + SemType anydata = types.anydata(); + if (SemTypes.containsBasicType(s, PredefinedType.ERROR)) { + if (types.isSubtype(s, Core.union(anydata, PredefinedType.ERROR))) { + return symTable.pureType; + } else { + return BUnionType.create(symTable.typeEnv(), null, symTable.anyType, symTable.errorType); + } + } else if (types.isSubtype(s, anydata)) { + return symTable.anydataType; + } else { + return symTable.anyType; } - return memberType; } public BType getRestMatchPatternConstraintType(BRecordType recordType, @@ -3075,7 +2976,7 @@ public BType getRestMatchPatternConstraintType(BRecordType recordType, } else if (constraintTypes.size() == 1) { restConstraintType = constraintTypes.iterator().next(); } else { - restConstraintType = BUnionType.create(null, constraintTypes); + restConstraintType = BUnionType.create(symTable.typeEnv(), null, constraintTypes); } return restVarSymbolMapType.tag == TypeTags.NONE ? restConstraintType : this.types.mergeTypes(restVarSymbolMapType, restConstraintType); @@ -3085,7 +2986,7 @@ BRecordType createRecordTypeForRestField(Location pos, SymbolEnv env, BRecordTyp List variableList, BType restConstraint) { BRecordTypeSymbol recordSymbol = createAnonRecordSymbol(env, pos); - BRecordType recordVarType = new BRecordType(recordSymbol); + BRecordType recordVarType = new BRecordType(symTable.typeEnv(), recordSymbol); recordSymbol.type = recordVarType; LinkedHashMap unMappedFields = new LinkedHashMap<>() {{ putAll(recordType.fields); @@ -3167,43 +3068,6 @@ private long setSymbolAsOptional(long existingFlags) { return Flags.asMask(unmaskedFlags); } - private boolean hasOnlyAnyDataTypedFields(BRecordType recordType) { - IsAnydataUniqueVisitor isAnydataUniqueVisitor = new IsAnydataUniqueVisitor(); - return isAnydataUniqueVisitor.visit(recordType); - } - - private boolean hasOnlyPureTypedFields(BRecordType recordType) { - IsPureTypeUniqueVisitor isPureTypeUniqueVisitor = new IsPureTypeUniqueVisitor(); - for (BField field : recordType.fields.values()) { - BType fieldType = field.type; - if (!isPureTypeUniqueVisitor.visit(fieldType)) { - return false; - } - isPureTypeUniqueVisitor.reset(); - } - return recordType.sealed || isPureTypeUniqueVisitor.visit(recordType); - } - - private boolean hasErrorTypedField(BRecordType recordType) { - for (BField field : recordType.fields.values()) { - BType type = field.type; - if (hasErrorType(type)) { - return true; - } - } - return hasErrorType(recordType.restFieldType); - } - - private boolean hasErrorType(BType type) { - BType referredType = Types.getImpliedType(type); - int tag = referredType.tag; - if (tag != TypeTags.UNION) { - return tag == TypeTags.ERROR; - } - - return ((BUnionType) referredType).getMemberTypes().stream().anyMatch(this::hasErrorType); - } - @Override public void visit(BLangErrorVariable errorVar) { if (errorVar.isDeclaredWithVar) { @@ -3282,9 +3146,9 @@ boolean validateErrorVariable(BLangErrorVariable errorVariable, SymbolEnv env) { detailType.add(possibleErrType.detailType); } BType errorDetailType = detailType.size() > 1 - ? BUnionType.create(null, detailType) + ? BUnionType.create(symTable.typeEnv(), null, detailType) : detailType.iterator().next(); - errorType = new BErrorType(null, errorDetailType); + errorType = new BErrorType(symTable.typeEnv(), null, errorDetailType); } else { errorType = possibleTypes.get(0); } @@ -3333,7 +3197,7 @@ boolean validateErrorVariable(BLangErrorVariable errorVariable, SymbolEnv env) { env.enclPkg.packageID, symTable.errorType, env.scope.owner, errorVariable.pos, SOURCE); // TODO: detail type need to be a union representing all details of members of `errorType` - errorVariable.setBType(new BErrorType(errorTypeSymbol, symTable.detailType)); + errorVariable.setBType(new BErrorType(symTable.typeEnv(), errorTypeSymbol, symTable.detailType)); return validateErrorVariable(errorVariable, env); } @@ -3360,7 +3224,7 @@ private boolean validateErrorVariable(BLangErrorVariable errorVariable, BErrorTy BLangVariable boundVar = errorDetailEntry.valueBindingPattern; if (entryField != null) { if ((entryField.symbol.flags & Flags.OPTIONAL) == Flags.OPTIONAL) { - boundVar.setBType(BUnionType.create(null, entryField.type, symTable.nilType)); + boundVar.setBType(BUnionType.create(symTable.typeEnv(), null, entryField.type, symTable.nilType)); } else { boundVar.setBType(entryField.type); } @@ -3372,7 +3236,8 @@ private boolean validateErrorVariable(BLangErrorVariable errorVariable, BErrorTy boundVar.setBType(symTable.semanticError); return false; } else { - boundVar.setBType(BUnionType.create(null, recordType.restFieldType, symTable.nilType)); + boundVar.setBType( + BUnionType.create(symTable.typeEnv(), null, recordType.restFieldType, symTable.nilType)); } } @@ -3388,7 +3253,7 @@ private boolean validateErrorVariable(BLangErrorVariable errorVariable, BErrorTy // union of keys whose values are not matched in error binding/match pattern. BTypeSymbol typeSymbol = createTypeSymbol(SymTag.TYPE, env); BType constraint = getRestMapConstraintType(detailFields, matchedDetailFields, recordType); - BMapType restType = new BMapType(TypeTags.MAP, constraint, typeSymbol); + BMapType restType = new BMapType(symTable.typeEnv(), TypeTags.MAP, constraint, typeSymbol); typeSymbol.type = restType; errorVariable.restDetail.setBType(restType); defineMemberNode(errorVariable.restDetail, env, restType); @@ -3401,7 +3266,7 @@ BRecordType getDetailAsARecordType(BErrorType errorType) { if (detailType.getKind() == TypeKind.RECORD) { return (BRecordType) detailType; } - BRecordType detailRecord = new BRecordType(null); + BRecordType detailRecord = new BRecordType(symTable.typeEnv(), null); BMapType detailMap = (BMapType) detailType; detailRecord.sealed = false; detailRecord.restFieldType = detailMap.constraint; @@ -3410,7 +3275,7 @@ BRecordType getDetailAsARecordType(BErrorType errorType) { private BType getRestMapConstraintType(Map errorDetailFields, Set matchedDetailFields, BRecordType recordType) { - BUnionType restUnionType = BUnionType.create(null); + BUnionType restUnionType = BUnionType.create(symTable.typeEnv(), null); if (!recordType.sealed) { BType referredRestFieldType = Types.getImpliedType(recordType.restFieldType); if (referredRestFieldType.tag == TypeTags.UNION) { @@ -3694,38 +3559,39 @@ private void populateLangLibInSymTable(BPackageSymbol packageSymbol) { } } + /** + * Checks if annotation type descriptor is valid. + *

+ * The type must be a subtype of one of the following three types: + *

    + *
  • true
  • + *
  • map<value:Cloneable>
  • + *
  • map<value:Cloneable>[]
  • + *
+ * + * @param type type to be checked + * @return boolean + */ public boolean isValidAnnotationType(BType type) { - type = Types.getImpliedType(type); - if (type == symTable.semanticError) { - return false; + SemType t = type.semType(); + if (SemTypes.isSubtype(types.semTypeCtx, t, symTable.trueType.semType())) { + return true; } - switch (type.tag) { - case TypeTags.MAP: - return types.isAssignable(((BMapType) type).constraint, symTable.cloneableType); - case TypeTags.RECORD: - BRecordType recordType = (BRecordType) type; - for (BField field : recordType.fields.values()) { - if (!types.isAssignable(field.type, symTable.cloneableType)) { - return false; - } - } - - BType recordRestType = recordType.restFieldType; - if (recordRestType == null || recordRestType == symTable.noType) { - return true; - } + SemType cloneable = Core.createCloneable(types.semTypeCtx); + if (SemTypes.isSubtypeSimple(t, PredefinedType.MAPPING)) { + return SemTypes.isSubtype(types.semTypeCtx, t, cloneable); + } - return types.isAssignable(recordRestType, symTable.cloneableType); - case TypeTags.ARRAY: - BType elementType = Types.getImpliedType(((BArrayType) type).eType); - if ((elementType.tag == TypeTags.MAP) || (elementType.tag == TypeTags.RECORD)) { - return isValidAnnotationType(elementType); - } - return false; + if (SemTypes.isSubtypeSimple(t, PredefinedType.LIST)) { + // Using projection to get T from T[] + SemType memberTy = Core.listMemberTypeInnerVal(types.semTypeCtx, t, PredefinedType.INT); + if (SemTypes.isSubtypeSimple(memberTy, PredefinedType.MAPPING)) { + return SemTypes.isSubtype(types.semTypeCtx, memberTy, cloneable); + } } - return types.isAssignable(type, symTable.trueType); + return false; } /** @@ -3840,20 +3706,6 @@ private BType getDetailType(SymbolEnv typeDefEnv, BLangErrorType errorTypeNode) .orElse(symTable.detailType); } - public void defineFields(List typeDefNodes, SymbolEnv pkgEnv) { - for (BLangNode typeDef : typeDefNodes) { - if (typeDef.getKind() == NodeKind.CLASS_DEFN) { - BLangClassDefinition classDefinition = (BLangClassDefinition) typeDef; - if (isObjectCtor(classDefinition)) { - continue; - } - defineFieldsOfClassDef(classDefinition, pkgEnv); - } else if (typeDef.getKind() == NodeKind.TYPE_DEFINITION) { - defineFields((BLangTypeDefinition) typeDef, pkgEnv); - } - } - } - public void defineFieldsOfClassDef(BLangClassDefinition classDefinition, SymbolEnv env) { SymbolEnv typeDefEnv = SymbolEnv.createClassEnv(classDefinition, classDefinition.symbol.scope, env); BObjectTypeSymbol tSymbol = (BObjectTypeSymbol) classDefinition.symbol; @@ -4268,7 +4120,7 @@ private void validateFieldsAndSetReadOnlyType(List typeDefNodes, Symb BSymbol symbol = typeDef.symbol; BStructureType structureType = (BStructureType) Types.getImpliedType(symbol.type); - if (Symbols.isFlagOn(structureType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(structureType.getFlags(), Flags.READONLY)) { if (structureType.tag != TypeTags.OBJECT) { continue; } @@ -4336,7 +4188,7 @@ private void validateFieldsAndSetReadOnlyType(List typeDefNodes, Symb if (allImmutableFields) { structureType.tsymbol.flags |= Flags.READONLY; - structureType.flags |= Flags.READONLY; + structureType.addFlags(Flags.READONLY); } } } @@ -4370,7 +4222,7 @@ private void setReadOnlynessOfClassDef(BLangClassDefinition classDef, SymbolEnv BObjectType objectType = (BObjectType) classDef.getBType(); Location pos = classDef.pos; - if (Symbols.isFlagOn(classDef.getBType().flags, Flags.READONLY)) { + if (Symbols.isFlagOn(classDef.getBType().getFlags(), Flags.READONLY)) { if (!types.isSelectivelyImmutableType(objectType, new HashSet<>(), pkgEnv.enclPkg.packageID)) { dlog.error(pos, DiagnosticErrorCode.INVALID_READONLY_OBJECT_TYPE, objectType); return; @@ -4386,13 +4238,13 @@ private void setReadOnlynessOfClassDef(BLangClassDefinition classDef, SymbolEnv for (BField field : fields) { if (!Symbols.isFlagOn(field.symbol.flags, Flags.FINAL) || - !Symbols.isFlagOn(field.type.flags, Flags.READONLY)) { + !Symbols.isFlagOn(field.type.getFlags(), Flags.READONLY)) { return; } } classDef.getBType().tsymbol.flags |= Flags.READONLY; - classDef.getBType().flags |= Flags.READONLY; + classDef.getBType().addFlags(Flags.READONLY); } } @@ -4404,11 +4256,11 @@ private void defineInvokableSymbol(BLangInvokableNode invokableNode, BInvokableS defineInvokableSymbolParams(invokableNode, funcSymbol, invokableEnv); if (Symbols.isFlagOn(funcSymbol.type.tsymbol.flags, Flags.ISOLATED)) { - funcSymbol.type.flags |= Flags.ISOLATED; + funcSymbol.type.addFlags(Flags.ISOLATED); } if (Symbols.isFlagOn(funcSymbol.type.tsymbol.flags, Flags.TRANSACTIONAL)) { - funcSymbol.type.flags |= Flags.TRANSACTIONAL; + funcSymbol.type.addFlags(Flags.TRANSACTIONAL); } } @@ -4581,7 +4433,7 @@ public void defineInvokableTypeNode(BLangFunctionTypeNode functionTypeNode, long bInvokableType.paramTypes = paramTypes; bInvokableType.retType = retType; bInvokableType.restType = restType; - bInvokableType.flags |= flags; + bInvokableType.addFlags(flags); functionTypeNode.setBType(bInvokableType); List allConstituentTypes = new ArrayList<>(paramTypes); @@ -4624,7 +4476,7 @@ void defineInvokableSymbolParams(BLangInvokableNode invokableNode, BInvokableSym functionTypeSymbol.restParam = invokableSymbol.restParam; restType = invokableSymbol.restParam.type; } - invokableSymbol.type = new BInvokableType(paramTypes, restType, retType, null); + invokableSymbol.type = new BInvokableType(symTable.typeEnv(), paramTypes, restType, retType, null); invokableSymbol.type.tsymbol = functionTypeSymbol; invokableSymbol.type.tsymbol.type = invokableSymbol.type; } @@ -4680,13 +4532,6 @@ public void defineTypeNarrowedSymbol(Location location, SymbolEnv targetEnv, BVa defineShadowedSymbol(location, varSymbol, targetEnv); } - private void defineSymbolWithCurrentEnvOwner(Location pos, BSymbol symbol) { - symbol.scope = new Scope(env.scope.owner); - if (symResolver.checkForUniqueSymbol(pos, env, symbol)) { - env.scope.define(symbol.name, symbol); - } - } - public BVarSymbol defineVarSymbol(Location pos, Set flagSet, BType varType, Name varName, SymbolEnv env, boolean isInternal) { return defineVarSymbol(pos, flagSet, varType, varName, varName, env, isInternal); @@ -4930,39 +4775,6 @@ private void validateResourceFunctionAttachedToObject(BLangFunction funcNode, BO } } - private StatementNode createAssignmentStmt(BLangSimpleVariable variable, BVarSymbol varSym, BSymbol fieldVar) { - //Create LHS reference variable - BLangSimpleVarRef varRef = (BLangSimpleVarRef) TreeBuilder.createSimpleVariableReferenceNode(); - varRef.pos = variable.pos; - varRef.variableName = (BLangIdentifier) createIdentifier(fieldVar.name.getValue()); - varRef.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode(); - varRef.symbol = fieldVar; - varRef.setBType(fieldVar.type); - - //Create RHS variable reference - BLangSimpleVarRef exprVar = (BLangSimpleVarRef) TreeBuilder.createSimpleVariableReferenceNode(); - exprVar.pos = variable.pos; - exprVar.variableName = (BLangIdentifier) createIdentifier(varSym.name.getValue()); - exprVar.pkgAlias = (BLangIdentifier) TreeBuilder.createIdentifierNode(); - exprVar.symbol = varSym; - exprVar.setBType(varSym.type); - - //Create assignment statement - BLangAssignment assignmentStmt = (BLangAssignment) TreeBuilder.createAssignmentNode(); - assignmentStmt.expr = exprVar; - assignmentStmt.pos = variable.pos; - assignmentStmt.setVariable(varRef); - return assignmentStmt; - } - - private IdentifierNode createIdentifier(String value) { - IdentifierNode node = TreeBuilder.createIdentifierNode(); - if (value != null) { - node.setValue(value); - } - return node; - } - private boolean validateFuncReceiver(BLangFunction funcNode) { if (funcNode.receiver == null) { return true; @@ -4999,11 +4811,6 @@ private Name getFuncSymbolOriginalName(BLangFunction funcNode) { return names.originalNameFromIdNode(funcNode.name); } - private Name getFieldSymbolName(BLangSimpleVariable receiver, BLangSimpleVariable variable) { - return Names.fromString(Symbols.getAttachedFuncSymbolName( - receiver.getBType().tsymbol.name.value, variable.name.value)); - } - public MarkdownDocAttachment getMarkdownDocAttachment(BLangMarkdownDocumentation docNode) { if (docNode == null) { return new MarkdownDocAttachment(0); @@ -5201,8 +5008,8 @@ private void defineReferencedFunction(Location location, Set flagSet, Symb // If not, define the function symbol within the object. // Take a copy of the symbol, with the new name, and the package ID same as the object type. - BInvokableSymbol funcSymbol = ASTBuilderUtil.duplicateFunctionDeclarationSymbol(referencedFuncSymbol, - typeDefSymbol, funcName, typeDefSymbol.pkgID, typeRef.pos, getOrigin(funcName)); + BInvokableSymbol funcSymbol = ASTBuilderUtil.duplicateFunctionDeclarationSymbol(symTable.typeEnv(), + referencedFuncSymbol, typeDefSymbol, funcName, typeDefSymbol.pkgID, typeRef.pos, getOrigin(funcName)); defineSymbol(typeRef.pos, funcSymbol, objEnv); // Create and define the parameters and receiver. This should be done after defining the function symbol. @@ -5316,12 +5123,12 @@ private void setTypeFromLambdaExpr(BLangVariable variable) { BLangFunction function = ((BLangLambdaFunction) variable.expr).function; BInvokableType invokableType = (BInvokableType) function.symbol.type; if (function.flagSet.contains(Flag.ISOLATED)) { - invokableType.flags |= Flags.ISOLATED; + invokableType.addFlags(Flags.ISOLATED); invokableType.tsymbol.flags |= Flags.ISOLATED; } if (function.flagSet.contains(Flag.TRANSACTIONAL)) { - invokableType.flags |= Flags.TRANSACTIONAL; + invokableType.addFlags(Flags.TRANSACTIONAL); invokableType.tsymbol.flags |= Flags.TRANSACTIONAL; } @@ -5370,7 +5177,7 @@ private boolean isInvalidIncludedTypeInClass(BType includedType) { } private boolean isImmutable(BObjectType objectType) { - if (Symbols.isFlagOn(objectType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(objectType.getFlags(), Flags.READONLY)) { return true; } @@ -5381,7 +5188,7 @@ private boolean isImmutable(BObjectType objectType) { for (BField field : fields) { if (!Symbols.isFlagOn(field.symbol.flags, Flags.FINAL) || - !Symbols.isFlagOn(field.type.flags, Flags.READONLY)) { + !Symbols.isFlagOn(field.type.getFlags(), Flags.READONLY)) { return false; } } @@ -5393,7 +5200,7 @@ private boolean isReadOnlyAndObjectIntersection(BIntersectionType referredType) BType effectiveType = referredType.effectiveType; if (Types.getImpliedType(effectiveType).tag != TypeTags.OBJECT || - !Symbols.isFlagOn(effectiveType.flags, Flags.READONLY)) { + !Symbols.isFlagOn(effectiveType.getFlags(), Flags.READONLY)) { return false; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java index 449ef9f0c2ad..c0536ed6796b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolResolver.java @@ -19,6 +19,7 @@ import io.ballerina.tools.diagnostics.DiagnosticCode; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.AttachPoint; import org.ballerinalang.model.elements.Flag; @@ -56,7 +57,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; import org.wso2.ballerinalang.compiler.semantics.model.types.BIntersectionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; @@ -418,7 +418,7 @@ public BSymbol resolveBinaryOperator(OperatorKind opKind, private BSymbol createEqualityOperator(OperatorKind opKind, BType lhsType, BType rhsType) { List paramTypes = Lists.of(lhsType, rhsType); BType retType = symTable.booleanType; - BInvokableType opType = new BInvokableType(paramTypes, retType, null); + BInvokableType opType = new BInvokableType(symTable.typeEnv(), paramTypes, retType, null); return new BOperatorSymbol(Names.fromString(opKind.value()), null, opType, null, symTable.builtinPos, VIRTUAL); } @@ -434,19 +434,19 @@ public BSymbol resolveOperator(Name name, List types) { private BSymbol createBinaryComparisonOperator(OperatorKind opKind, BType lhsType, BType rhsType) { List paramTypes = Lists.of(lhsType, rhsType); - BInvokableType opType = new BInvokableType(paramTypes, symTable.booleanType, null); + BInvokableType opType = new BInvokableType(symTable.typeEnv(), paramTypes, symTable.booleanType, null); return new BOperatorSymbol(Names.fromString(opKind.value()), null, opType, null, symTable.builtinPos, VIRTUAL); } private BSymbol createBinaryOperator(OperatorKind opKind, BType lhsType, BType rhsType, BType retType) { List paramTypes = Lists.of(lhsType, rhsType); - BInvokableType opType = new BInvokableType(paramTypes, retType, null); + BInvokableType opType = new BInvokableType(symTable.typeEnv(), paramTypes, retType, null); return new BOperatorSymbol(Names.fromString(opKind.value()), null, opType, null, symTable.builtinPos, VIRTUAL); } BSymbol createUnaryOperator(OperatorKind kind, BType type, BType retType) { List paramTypes = Lists.of(type); - BInvokableType opType = new BInvokableType(paramTypes, retType, null); + BInvokableType opType = new BInvokableType(symTable.typeEnv(), paramTypes, retType, null); return new BOperatorSymbol(Names.fromString(kind.value()), null, opType, null, symTable.builtinPos, VIRTUAL); } @@ -564,9 +564,9 @@ private BType resolveTypeNode(BLangType typeNode, AnalyzerData data, SymbolEnv e BUnionType unionType = (BUnionType) refType; unionType.add(symTable.nilType); } else if (typeNode.nullable && resultType.tag != TypeTags.JSON && resultType.tag != TypeTags.ANY) { - resultType = BUnionType.create(null, resultType, symTable.nilType); + resultType = BUnionType.create(symTable.typeEnv(), null, resultType, symTable.nilType); } else if (typeNode.nullable && refType.tag != TypeTags.JSON && refType.tag != TypeTags.ANY) { - resultType = BUnionType.create(null, resultType, symTable.nilType); + resultType = BUnionType.create(symTable.typeEnv(), null, resultType, symTable.nilType); } } @@ -1046,12 +1046,13 @@ public void bootstrapAnydataType() { continue; } BUnionType type = (BUnionType) Types.getImpliedType(entry.symbol.type); - symTable.anydataType = new BAnydataType(type); + symTable.anydataType = new BAnydataType(types.semTypeCtx, type); Optional immutableType = Types.getImmutableType(symTable, PackageID.ANNOTATIONS, type); if (immutableType.isPresent()) { Types.addImmutableType(symTable, PackageID.ANNOTATIONS, symTable.anydataType, immutableType.get()); } - symTable.anydataOrReadonly = BUnionType.create(null, symTable.anydataType, symTable.readonlyType); + symTable.anydataOrReadonly = + BUnionType.create(symTable.typeEnv(), null, symTable.anydataType, symTable.readonlyType); entry.symbol.type = symTable.anydataType; entry.symbol.origin = BUILTIN; @@ -1070,7 +1071,7 @@ public void bootstrapJsonType() { continue; } BUnionType type = (BUnionType) Types.getImpliedType(entry.symbol.type); - symTable.jsonType = new BJSONType(type); + symTable.jsonType = new BJSONType(types.semTypeCtx, type); Optional immutableType = Types.getImmutableType(symTable, PackageID.ANNOTATIONS, type); if (immutableType.isPresent()) { @@ -1098,21 +1099,24 @@ public void bootstrapCloneableType() { new BTypeSymbol(SymTag.TYPE, Flags.PUBLIC, Names.CLONEABLE, PackageID.VALUE, symTable.cloneableType, symTable.langValueModuleSymbol, symTable.builtinPos, BUILTIN); - symTable.detailType = new BMapType(TypeTags.MAP, symTable.cloneableType, null); - symTable.errorType = new BErrorType(null, symTable.detailType); + symTable.detailType = new BMapType(symTable.typeEnv(), TypeTags.MAP, symTable.cloneableType, null); + symTable.errorType = new BErrorType(symTable.typeEnv(), null, symTable.detailType); symTable.errorType.tsymbol = new BErrorTypeSymbol(SymTag.ERROR, Flags.PUBLIC, Names.ERROR, symTable.rootPkgSymbol.pkgID, symTable.errorType, symTable.rootPkgSymbol, symTable.builtinPos , BUILTIN); - symTable.errorOrNilType = BUnionType.create(null, symTable.errorType, symTable.nilType); - symTable.anyOrErrorType = BUnionType.create(null, symTable.anyType, symTable.errorType); + symTable.errorOrNilType = + BUnionType.create(symTable.typeEnv(), null, symTable.errorType, symTable.nilType); + symTable.anyOrErrorType = + BUnionType.create(symTable.typeEnv(), null, symTable.anyType, symTable.errorType); - symTable.mapAllType = new BMapType(TypeTags.MAP, symTable.anyOrErrorType, null); - symTable.arrayAllType = new BArrayType(symTable.anyOrErrorType); + symTable.mapAllType = new BMapType(symTable.typeEnv(), TypeTags.MAP, symTable.anyOrErrorType, null); + symTable.arrayAllType = new BArrayType(symTable.typeEnv(), symTable.anyOrErrorType); symTable.typeDesc.constraint = symTable.anyOrErrorType; symTable.futureType.constraint = symTable.anyOrErrorType; - symTable.pureType = BUnionType.create(null, symTable.anydataType, symTable.errorType); + symTable.pureType = + BUnionType.create(symTable.typeEnv(), null, symTable.anydataType, symTable.errorType); return; } throw new IllegalStateException("built-in 'lang.value:Cloneable' type not found"); @@ -1198,7 +1202,7 @@ public BType transform(BLangArrayType arrayTypeNode, AnalyzerData data) { data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, arrayTypeNode.pos, SOURCE); BArrayType arrType; if (arrayTypeNode.sizes.isEmpty()) { - arrType = new BArrayType(resultType, arrayTypeSymbol); + arrType = new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol); } else { BLangExpression size = arrayTypeNode.sizes.get(i); if (size.getKind() == NodeKind.LITERAL || size.getKind() == NodeKind.NUMERIC_LITERAL) { @@ -1211,7 +1215,8 @@ public BType transform(BLangArrayType arrayTypeNode, AnalyzerData data) { } else { arrayState = BArrayState.CLOSED; } - arrType = new BArrayType(resultType, arrayTypeSymbol, sizeIndicator, arrayState); + arrType = + new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol, sizeIndicator, arrayState); } else { if (size.getKind() != NodeKind.SIMPLE_VARIABLE_REF) { dlog.error(size.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, symTable.intType, @@ -1261,7 +1266,8 @@ public BType transform(BLangArrayType arrayTypeNode, AnalyzerData data) { } else { length = (int) lengthCheck; } - arrType = new BArrayType(resultType, arrayTypeSymbol, length, BArrayState.CLOSED); + arrType = + new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol, length, BArrayState.CLOSED); } } arrayTypeSymbol.type = arrType; @@ -1291,7 +1297,7 @@ public BType transform(BLangUnionTypeNode unionTypeNode, AnalyzerData data) { Names.EMPTY, data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, unionTypeNode.pos, SOURCE); - BUnionType unionType = BUnionType.create(unionTypeSymbol, memberTypes); + BUnionType unionType = BUnionType.create(symTable.typeEnv(), unionTypeSymbol, memberTypes); unionTypeSymbol.type = unionType; markParameterizedType(unionType, memberTypes); @@ -1328,7 +1334,7 @@ public BType transform(BLangObjectTypeNode objectTypeNode, AnalyzerData data) { BTypeSymbol objectSymbol = Symbols.createObjectSymbol(Flags.asMask(flags), Names.EMPTY, data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, objectTypeNode.pos, SOURCE); - BObjectType objectType = new BObjectType(objectSymbol, typeFlags); + BObjectType objectType = new BObjectType(symTable.typeEnv(), objectSymbol, typeFlags); objectSymbol.type = objectType; objectTypeNode.symbol = objectSymbol; @@ -1348,7 +1354,7 @@ public BType transform(BLangRecordTypeNode recordTypeNode, AnalyzerData data) { data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, recordTypeNode.pos, recordTypeNode.isAnonymous ? VIRTUAL : SOURCE); - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordSymbol); recordSymbol.type = recordType; recordTypeNode.symbol = recordSymbol; @@ -1375,7 +1381,7 @@ public BType transform(BLangStreamType streamTypeNode, AnalyzerData data) { return symTable.noType; } - BType streamType = new BStreamType(TypeTags.STREAM, constraintType, error, null); + BType streamType = new BStreamType(symTable.typeEnv(), TypeTags.STREAM, constraintType, error, null); BTypeSymbol typeSymbol = type.tsymbol; streamType.tsymbol = Symbols.createTypeSymbol(typeSymbol.tag, typeSymbol.flags, typeSymbol.name, typeSymbol.originalName, typeSymbol.pkgID, streamType, @@ -1398,7 +1404,7 @@ public BType transform(BLangTableTypeNode tableTypeNode, AnalyzerData data) { return symTable.noType; } - BTableType tableType = new BTableType(TypeTags.TABLE, constraintType, null); + BTableType tableType = new BTableType(symTable.typeEnv(), constraintType, null); BTypeSymbol typeSymbol = type.tsymbol; tableType.tsymbol = Symbols.createTypeSymbol(SymTag.TYPE, Flags.asMask(EnumSet.noneOf(Flag.class)), typeSymbol.name, typeSymbol.originalName, typeSymbol.pkgID, @@ -1436,22 +1442,7 @@ public BType transform(BLangTableTypeNode tableTypeNode, AnalyzerData data) { @Override public BType transform(BLangFiniteTypeNode finiteTypeNode, AnalyzerData data) { - BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, - Flags.asMask(EnumSet.of(Flag.PUBLIC)), Names.EMPTY, - data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, - finiteTypeNode.pos, SOURCE); - - // In case we encounter unary expressions in finite type, we will be replacing them with numeric literals - // Note: calling semanticAnalyzer form symbolResolver is a temporary fix. - semanticAnalyzer.analyzeNode(finiteTypeNode, data.env); - - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - for (BLangExpression expressionOrLiteral : finiteTypeNode.valueSpace) { - finiteType.addValue(expressionOrLiteral); - } - finiteTypeSymbol.type = finiteType; - - return finiteType; + return typeResolver.resolveSingletonType(finiteTypeNode, data.env); } @Override @@ -1481,9 +1472,9 @@ public BType transform(BLangTupleTypeNode tupleTypeNode, AnalyzerData data) { } List tupleMembers = new ArrayList<>(); members.forEach(member -> tupleMembers.add(new BTupleMember(member.getBType(), - new BVarSymbol(member.getBType().flags, member.symbol.name, member.symbol.pkgID, member.getBType(), + new BVarSymbol(member.getBType().getFlags(), member.symbol.name, member.symbol.pkgID, member.getBType(), member.symbol.owner, member.pos, SOURCE)))); - BTupleType tupleType = new BTupleType(tupleTypeSymbol, tupleMembers); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, tupleMembers); tupleTypeSymbol.type = tupleType; if (tupleTypeNode.restParamType != null) { @@ -1530,8 +1521,8 @@ public BType transform(BLangErrorType errorTypeNode, AnalyzerData data) { symbolEnter.defineSymbol(errorTypeNode.pos, errorTypeSymbol, data.env); } - BErrorType errorType = new BErrorType(errorTypeSymbol, detailType); - errorType.flags |= errorTypeSymbol.flags; + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorTypeSymbol, detailType); + errorType.addFlags(errorTypeSymbol.flags); errorTypeSymbol.type = errorType; markParameterizedType(errorType, detailType); @@ -1557,11 +1548,11 @@ public BType transform(BLangConstrainedType constrainedTypeNode, AnalyzerData da BType constrainedType; if (type.tag == TypeTags.FUTURE) { - constrainedType = new BFutureType(TypeTags.FUTURE, constraintType, null); + constrainedType = new BFutureType(symTable.typeEnv(), constraintType, null); } else if (type.tag == TypeTags.MAP) { - constrainedType = new BMapType(TypeTags.MAP, constraintType, null); + constrainedType = new BMapType(symTable.typeEnv(), TypeTags.MAP, constraintType, null); } else if (type.tag == TypeTags.TYPEDESC) { - constrainedType = new BTypedescType(constraintType, null); + constrainedType = new BTypedescType(symTable.typeEnv(), constraintType, null); } else if (type.tag == TypeTags.XML) { if (Types.getImpliedType(constraintType).tag == TypeTags.PARAMETERIZED_TYPE) { BType typedescType = ((BParameterizedType) constraintType).paramSymbol.type; @@ -1584,32 +1575,11 @@ public BType transform(BLangConstrainedType constrainedTypeNode, AnalyzerData da } public void validateXMLConstraintType(BType type, Location pos) { - BType constraintType = Types.getImpliedType(type); - int constrainedTag = constraintType.tag; - - if (constrainedTag == TypeTags.UNION) { - checkUnionTypeForXMLSubTypes((BUnionType) constraintType, pos); - return; - } - - if (!TypeTags.isXMLTypeTag(constrainedTag) && constrainedTag != TypeTags.NEVER) { + if (!SemTypeHelper.isSubtypeSimple(type, PredefinedType.XML)) { dlog.error(pos, DiagnosticErrorCode.INCOMPATIBLE_TYPE_CONSTRAINT, symTable.xmlType, type); } } - private void checkUnionTypeForXMLSubTypes(BUnionType constraintUnionType, Location pos) { - for (BType memberType : constraintUnionType.getMemberTypes()) { - memberType = Types.getImpliedType(memberType); - if (memberType.tag == TypeTags.UNION) { - checkUnionTypeForXMLSubTypes((BUnionType) memberType, pos); - } - if (!TypeTags.isXMLTypeTag(memberType.tag)) { - dlog.error(pos, DiagnosticErrorCode.INCOMPATIBLE_TYPE_CONSTRAINT, symTable.xmlType, - constraintUnionType); - } - } - } - @Override public BType transform(BLangUserDefinedType userDefinedTypeNode, AnalyzerData data) { String name = userDefinedTypeNode.typeName.value; @@ -1687,7 +1657,7 @@ public BType transform(BLangUserDefinedType userDefinedTypeNode, AnalyzerData da null, func.symbol, tempSymbol.pos, VIRTUAL); tSymbol.type = new BParameterizedType(paramValType, (BVarSymbol) tempSymbol, tSymbol, tempSymbol.name, parameterizedTypeInfo.index); - tSymbol.type.flags |= Flags.PARAMETERIZED; + tSymbol.type.addFlags(Flags.PARAMETERIZED); userDefinedTypeNode.symbol = tSymbol; return tSymbol.type; @@ -1729,7 +1699,7 @@ public BType transform(BLangUserDefinedType userDefinedTypeNode, AnalyzerData da tempSymbol.pos, VIRTUAL); tSymbol.type = new BParameterizedType(paramValType, (BVarSymbol) tempSymbol, tSymbol, tempSymbol.name, parameterizedTypeInfo.index); - tSymbol.type.flags |= Flags.PARAMETERIZED; + tSymbol.type.addFlags(Flags.PARAMETERIZED); userDefinedTypeNode.symbol = tSymbol; return tSymbol.type; } @@ -1759,8 +1729,8 @@ public BType transform(BLangUserDefinedType userDefinedTypeNode, AnalyzerData da if (symbol.kind == SymbolKind.TYPE_DEF && !Symbols.isFlagOn(symbol.flags, Flags.ANONYMOUS) && !isCloneableTypeDef) { BType referenceType = ((BTypeDefinitionSymbol) symbol).referenceType; - referenceType.flags |= symbol.type.flags; - referenceType.tsymbol.flags |= symbol.type.flags; + referenceType.addFlags(symbol.type.getFlags()); + referenceType.tsymbol.flags |= symbol.type.getFlags(); return referenceType; } @@ -1813,8 +1783,8 @@ public BType transform(BLangFunctionTypeNode functionTypeNode, AnalyzerData data BInvokableTypeSymbol invokableTypeSymbol; BInvokableType invokableType; if (functionTypeNode.flagSet.contains(Flag.ANY_FUNCTION)) { - invokableType = new BInvokableType(null, null, null, null); - invokableType.flags = Flags.asMask(functionTypeNode.flagSet); + invokableType = new BInvokableType(symTable.typeEnv(), List.of(), null, null, null); + invokableType.setFlags(Flags.asMask(functionTypeNode.flagSet)); invokableTypeSymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, Flags.asMask(functionTypeNode.flagSet), env.enclPkg.symbol.pkgID, invokableType, @@ -1828,7 +1798,7 @@ public BType transform(BLangFunctionTypeNode functionTypeNode, AnalyzerData data Flags.asMask(functionTypeNode.flagSet), env.enclPkg.symbol.pkgID, functionTypeNode.getBType(), env.scope.owner, functionTypeNode.pos, VIRTUAL); - invokableType = new BInvokableType(invokableTypeSymbol); + invokableType = new BInvokableType(symTable.typeEnv(), invokableTypeSymbol); invokableTypeSymbol.type = invokableType; invokableTypeSymbol.name = Names.fromString(anonymousModelHelper.getNextAnonymousTypeKey(env.enclPkg.packageID)); @@ -1888,9 +1858,7 @@ public BSymbol getBinaryEqualityForTypeSets(OperatorKind opKind, BType lhsType, break; case REF_EQUAL: case REF_NOT_EQUAL: - validEqualityIntersectionExists = - types.getTypeIntersection(Types.IntersectionContext.compilerInternalIntersectionTestContext(), - lhsType, rhsType, env) != symTable.semanticError; + validEqualityIntersectionExists = types.intersectionExists(lhsType.semType(), rhsType.semType()); break; default: return symTable.notFoundSymbol; @@ -1949,7 +1917,7 @@ public BSymbol getBitwiseShiftOpsForTypeSets(OperatorKind opKind, BType lhsType, private BSymbol createShiftOperator(OperatorKind opKind, BType lhsType, BType rhsType) { if (lhsType.isNullable() || rhsType.isNullable()) { - BType intOptional = BUnionType.create(null, symTable.intType, symTable.nilType); + BType intOptional = BUnionType.create(symTable.typeEnv(), null, symTable.intType, symTable.nilType); return createBinaryOperator(opKind, lhsType, rhsType, intOptional); } return createBinaryOperator(opKind, lhsType, rhsType, symTable.intType); @@ -1997,7 +1965,7 @@ public BSymbol getArithmeticOpsForTypeSets(OperatorKind opKind, BType lhsType, B BType returnType = compatibleType1.tag < compatibleType2.tag ? compatibleType2 : compatibleType1; if (lhsType.isNullable() || rhsType.isNullable()) { - returnType = BUnionType.create(null, returnType, symTable.nilType); + returnType = BUnionType.create(symTable.typeEnv(), null, returnType, symTable.nilType); } return createBinaryOperator(opKind, lhsType, rhsType, returnType); @@ -2038,7 +2006,8 @@ public BSymbol getUnaryOpsForTypeSets(OperatorKind opKind, BType type) { } if (type.isNullable()) { BType compatibleType = types.findCompatibleType(types.getSafeType(type, true, false)); - return createUnaryOperator(opKind, type, BUnionType.create(null, compatibleType, symTable.nilType)); + return createUnaryOperator(opKind, type, + BUnionType.create(symTable.typeEnv(), null, compatibleType, symTable.nilType)); } return createUnaryOperator(opKind, type, types.findCompatibleType(type)); } @@ -2067,7 +2036,7 @@ public BSymbol getBinaryBitwiseOpsForTypeSets(OperatorKind opKind, BType lhsType case TypeTags.UNSIGNED32_INT: return createBinaryOperator(opKind, lhsType, rhsType, lhsType); } - + switch (referredRhsType.tag) { case TypeTags.UNSIGNED8_INT: case TypeTags.BYTE: @@ -2075,16 +2044,18 @@ public BSymbol getBinaryBitwiseOpsForTypeSets(OperatorKind opKind, BType lhsType case TypeTags.UNSIGNED32_INT: return createBinaryOperator(opKind, lhsType, rhsType, rhsType); } - + if (referredLhsType.isNullable() || referredRhsType.isNullable()) { - BType intOptional = BUnionType.create(null, symTable.intType, symTable.nilType); + BType intOptional = + BUnionType.create(symTable.typeEnv(), null, symTable.intType, symTable.nilType); return createBinaryOperator(opKind, lhsType, rhsType, intOptional); } return createBinaryOperator(opKind, lhsType, rhsType, symTable.intType); case BITWISE_OR: case BITWISE_XOR: if (referredLhsType.isNullable() || referredRhsType.isNullable()) { - BType intOptional = BUnionType.create(null, symTable.intType, symTable.nilType); + BType intOptional = + BUnionType.create(symTable.typeEnv(), null, symTable.intType, symTable.nilType); return createBinaryOperator(opKind, lhsType, rhsType, intOptional); } return createBinaryOperator(opKind, lhsType, rhsType, symTable.intType); @@ -2108,8 +2079,7 @@ public BSymbol getBinaryComparisonOpForTypeSets(OperatorKind opKind, BType lhsTy case LESS_EQUAL: case GREATER_THAN: case GREATER_EQUAL: - validOrderedTypesExist = types.isOrderedType(lhsType, false) && - types.isOrderedType(rhsType, false) && types.isSameOrderedType(lhsType, rhsType); + validOrderedTypesExist = types.comparable(lhsType, rhsType); break; default: return symTable.notFoundSymbol; @@ -2166,16 +2136,16 @@ public boolean isBinaryComparisonOperator(OperatorKind binaryOpKind) { } public boolean markParameterizedType(BType type, BType constituentType) { - if (Symbols.isFlagOn(constituentType.flags, Flags.PARAMETERIZED)) { + if (Symbols.isFlagOn(constituentType.getFlags(), Flags.PARAMETERIZED)) { type.tsymbol.flags |= Flags.PARAMETERIZED; - type.flags |= Flags.PARAMETERIZED; + type.addFlags(Flags.PARAMETERIZED); return true; } return false; } public void markParameterizedType(BType enclosingType, Collection constituentTypes) { - if (Symbols.isFlagOn(enclosingType.flags, Flags.PARAMETERIZED)) { + if (Symbols.isFlagOn(enclosingType.getFlags(), Flags.PARAMETERIZED)) { return; } @@ -2202,7 +2172,7 @@ private BSymbol resolveOperator(ScopeEntry entry, List typeList) { for (int i = 0; i < typeList.size(); i++) { BType t = Types.getImpliedType(typeList.get(i)); if ((t.getKind() == TypeKind.UNION) && (opType.paramTypes.get(i).getKind() == TypeKind.UNION)) { - if (!this.types.isSameType(t, opType.paramTypes.get(i))) { + if (!this.types.isSameTypeIncludingTags(t, opType.paramTypes.get(i))) { match = false; } } else if (t.tag != opType.paramTypes.get(i).tag) { @@ -2383,11 +2353,11 @@ private BType computeIntersectionType(BLangIntersectionTypeNode intersectionType } if (types.isInherentlyImmutableType(potentialIntersectionType) || - (Symbols.isFlagOn(potentialIntersectionType.flags, Flags.READONLY) && + (Symbols.isFlagOn(potentialIntersectionType.getFlags(), Flags.READONLY) && // For objects, a new type has to be created. !types.isSubTypeOfBaseType(potentialIntersectionType, TypeTags.OBJECT))) { BTypeSymbol effectiveTypeTSymbol = potentialIntersectionType.tsymbol; - return createIntersectionType(potentialIntersectionType, constituentBTypes, effectiveTypeTSymbol.pkgID, + return createIntersectionType(potentialIntersectionType, constituentBTypes, effectiveTypeTSymbol.pkgID, effectiveTypeTSymbol.owner, intersectionTypeNode.pos); } @@ -2457,8 +2427,8 @@ private BIntersectionType createIntersectionType(BType effectiveType, BTypeSymbol intersectionTypeSymbol = Symbols.createTypeSymbol(SymTag.INTERSECTION_TYPE, 0, Names.EMPTY, pkgId, null, owner, pos, VIRTUAL); - BIntersectionType intersectionType = - new BIntersectionType(intersectionTypeSymbol, constituentBTypes, effectiveType, effectiveType.flags); + BIntersectionType intersectionType = new BIntersectionType(intersectionTypeSymbol, constituentBTypes, + effectiveType, effectiveType.getFlags()); intersectionTypeSymbol.type = intersectionType; return intersectionType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java index cef5cdbdf1a3..4429826f62da 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeChecker.java @@ -20,6 +20,24 @@ import io.ballerina.identifier.Utils; import io.ballerina.tools.diagnostics.DiagnosticCode; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Core; +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableString; +import io.ballerina.types.EnumerableType; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.CharStringSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.NonCharStringSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.StringSubtype; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.AttachPoint; import org.ballerinalang.model.elements.Flag; @@ -85,6 +103,7 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLSubType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.tree.BLangAnnotationAttachment; import org.wso2.ballerinalang.compiler.tree.BLangClassDefinition; import org.wso2.ballerinalang.compiler.tree.BLangFunction; @@ -193,11 +212,11 @@ import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; @@ -212,6 +231,10 @@ import javax.xml.XMLConstants; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.Core.widenToBasicTypes; import static org.ballerinalang.model.symbols.SymbolOrigin.SOURCE; import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL; import static org.ballerinalang.util.diagnostic.DiagnosticErrorCode.INVALID_NUM_INSERTIONS; @@ -254,6 +277,7 @@ public class TypeChecker extends SimpleBLangNodeAnalyzer key) { @@ -343,6 +368,7 @@ public TypeChecker(CompilerContext context, CompilerContext.Key key this.missingNodesHelper = BLangMissingNodesHelper.getInstance(context); this.unifier = new Unifier(); this.queryTypeChecker = null; + this.typeEnv = types.typeEnv(); } private BType checkExpr(BLangExpression expr, SymbolEnv env, AnalyzerData data) { @@ -530,52 +556,39 @@ private void checkXMLNamespacePrefixes(List filters, Anal } private int getPreferredMemberTypeTag(BFiniteType finiteType) { - for (int i = TypeTags.INT; i <= TypeTags.DECIMAL; i++) { - for (BLangExpression valueExpr : finiteType.getValueSpace()) { - int typeTag = Types.getImpliedType(valueExpr.getBType()).tag; - if (typeTag > TypeTags.DECIMAL) { - continue; - } - - if (typeTag == i) { - return i; - } - } + BasicTypeBitSet basicTypeBitSet = widenToBasicTypes(finiteType.semType()); + if ((basicTypeBitSet.bitset & PredefinedType.INT.bitset) != 0) { + return TypeTags.INT; + } else if ((basicTypeBitSet.bitset & PredefinedType.FLOAT.bitset) != 0) { + return TypeTags.FLOAT; + } else if ((basicTypeBitSet.bitset & PredefinedType.DECIMAL.bitset) != 0) { + return TypeTags.DECIMAL; + } else { + return TypeTags.NONE; } - return TypeTags.NONE; } - private BType getFiniteTypeMatchWithIntType(BLangLiteral literalExpr, BFiniteType finiteType, AnalyzerData data) { + private BType getFiniteTypeMatchWithIntType(BLangNumericLiteral literalExpr, BFiniteType finiteType, + AnalyzerData data) { if (literalAssignableToFiniteType(literalExpr, finiteType, TypeTags.INT)) { setLiteralValueForFiniteType(literalExpr, symTable.intType, data); return symTable.intType; - } else if (literalAssignableToFiniteType(literalExpr, finiteType, TypeTags.BYTE)) { - setLiteralValueForFiniteType(literalExpr, symTable.byteType, data); - return symTable.byteType; - - } else { - for (int tag = TypeTags.SIGNED32_INT; tag <= TypeTags.UNSIGNED8_INT; tag++) { - if (literalAssignableToFiniteType(literalExpr, finiteType, tag)) { - setLiteralValueForFiniteType(literalExpr, symTable.getTypeFromTag(tag), data); - return symTable.getTypeFromTag(tag); - } - } } return symTable.noType; } - private BType getFiniteTypeMatchWithIntLiteral(BLangLiteral literalExpr, BFiniteType finiteType, + private BType getFiniteTypeMatchWithIntLiteral(BLangNumericLiteral literalExpr, BFiniteType finiteType, Object literalValue, BType compatibleType, AnalyzerData data) { BType intLiteralType = getFiniteTypeMatchWithIntType(literalExpr, finiteType, data); if (intLiteralType != symTable.noType) { return intLiteralType; } - + int typeTag = getPreferredMemberTypeTag(finiteType); if (typeTag == TypeTags.NONE) { return symTable.intType; } - + if (literalAssignableToFiniteType(literalExpr, finiteType, typeTag)) { BType type = symTable.getTypeFromTag(typeTag); setLiteralValueForFiniteType(literalExpr, type, data); @@ -596,7 +609,7 @@ private BType getFiniteTypeMatchWithIntLiteral(BLangLiteral literalExpr, BFinite return compatibleType; } - private BType silentIntTypeCheck(BLangLiteral literalExpr, Object literalValue, BType expType, + private BType silentIntTypeCheck(BLangNumericLiteral literalExpr, Object literalValue, BType expType, AnalyzerData data) { boolean prevNonErrorLoggingCheck = data.commonAnalyzerData.nonErrorLoggingCheck; data.commonAnalyzerData.nonErrorLoggingCheck = true; @@ -615,37 +628,32 @@ private BType silentIntTypeCheck(BLangLiteral literalExpr, Object literalValue, return exprCompatibleType; } - private BType silentCompatibleLiteralTypeCheck(BFiniteType finiteType, BLangLiteral literalExpr, - Object literalValue, AnalyzerData data) { + private BType checkIfOutOfRangeAndReturnType(BFiniteType finiteType, BLangNumericLiteral literalExpr, + Object literalValue, AnalyzerData data) { BType resIntegerLiteralType = symTable.semanticError; List compatibleTypes = new ArrayList<>(); - for (BLangExpression valueExpr : finiteType.getValueSpace()) { - resIntegerLiteralType = silentIntTypeCheck(literalExpr, literalValue, valueExpr.getBType(), data); + Set broadTypes = SemTypeHelper.broadTypes(finiteType, symTable); + for (BType broadType : broadTypes) { + resIntegerLiteralType = silentIntTypeCheck(literalExpr, literalValue, broadType, data); if (resIntegerLiteralType != symTable.semanticError) { compatibleTypes.add(resIntegerLiteralType); } } + for (int i = TypeTags.INT; i <= TypeTags.DECIMAL; i++) { - for (BType type: compatibleTypes) { + for (BType type : compatibleTypes) { if (Types.getReferredType(type).tag == i) { return type; } } } - return resIntegerLiteralType; - } - private BType checkIfOutOfRangeAndReturnType(BFiniteType finiteType, BLangLiteral literalExpr, Object literalValue, - AnalyzerData data) { - BType compatibleType = silentCompatibleLiteralTypeCheck(finiteType, literalExpr, literalValue, data); - if (compatibleType == symTable.semanticError) { - dlog.error(literalExpr.pos, DiagnosticErrorCode.OUT_OF_RANGE, literalExpr.originalValue, - literalExpr.getBType()); - } - return compatibleType; + dlog.error(literalExpr.pos, DiagnosticErrorCode.OUT_OF_RANGE, literalExpr.originalValue, + literalExpr.getBType()); + return resIntegerLiteralType; } - public BType getIntegerLiteralType(BLangLiteral literalExpr, Object literalValue, BType expType, + public BType getIntegerLiteralType(BLangNumericLiteral literalExpr, Object literalValue, BType expType, AnalyzerData data) { BType expectedType = Types.getImpliedType(expType); if (expectedType.tag == TypeTags.BYTE || TypeTags.isIntegerTypeTag(expectedType.tag)) { @@ -662,8 +670,8 @@ public BType getIntegerLiteralType(BLangLiteral literalExpr, Object literalValue expectedType); return symTable.semanticError; } - if (literalValue instanceof Double doubleValue) { - literalExpr.value = doubleValue.doubleValue(); + if (literalValue instanceof Double) { + literalExpr.value = literalValue; } else { literalExpr.value = ((Long) literalValue).doubleValue(); } @@ -727,7 +735,7 @@ public BType getIntegerLiteralType(BLangLiteral literalExpr, Object literalValue return symTable.intType; } - public BType getTypeOfLiteralWithFloatDiscriminator(BLangLiteral literalExpr, Object literalValue, + public BType getTypeOfLiteralWithFloatDiscriminator(BLangNumericLiteral literalExpr, Object literalValue, BType expType, AnalyzerData data) { String numericLiteral = NumericLiteralSupport.stripDiscriminator(String.valueOf(literalValue)); if (!types.validateFloatLiteral(literalExpr.pos, numericLiteral)) { @@ -751,7 +759,7 @@ public BType getTypeOfLiteralWithFloatDiscriminator(BLangLiteral literalExpr, Ob return symTable.floatType; } - public BType getTypeOfLiteralWithDecimalDiscriminator(BLangLiteral literalExpr, Object literalValue, + public BType getTypeOfLiteralWithDecimalDiscriminator(BLangNumericLiteral literalExpr, Object literalValue, BType expType, AnalyzerData data) { literalExpr.value = NumericLiteralSupport.stripDiscriminator(String.valueOf(literalValue)); if (!types.isValidDecimalNumber(literalExpr.pos, literalExpr.value.toString())) { @@ -774,8 +782,8 @@ public BType getTypeOfLiteralWithDecimalDiscriminator(BLangLiteral literalExpr, return symTable.decimalType; } - public BType getTypeOfDecimalFloatingPointLiteral(BLangLiteral literalExpr, Object literalValue, BType expType, - AnalyzerData data) { + public BType getTypeOfDecimalFloatingPointLiteral(BLangNumericLiteral literalExpr, Object literalValue, + BType expType, AnalyzerData data) { BType expectedType = Types.getImpliedType(expType); String numericLiteral = String.valueOf(literalValue); if (expectedType != null) { @@ -791,25 +799,22 @@ public BType getTypeOfDecimalFloatingPointLiteral(BLangLiteral literalExpr, Obje } return symTable.floatType; } else if (expectedType.tag == TypeTags.FINITE) { - BFiniteType finiteType = (BFiniteType) expectedType; - for (int tag = TypeTags.FLOAT; tag <= TypeTags.DECIMAL; tag++) { - BType literalValueType = null; - for (BLangExpression valueExpr : finiteType.getValueSpace()) { - if (valueExpr.getBType().tag == tag) { - if (types.checkLiteralAssignabilityBasedOnType((BLangLiteral) valueExpr, literalExpr)) { - BType valueType = setLiteralValueAndGetType(literalExpr, - symTable.getTypeFromTag(tag), data); - setLiteralValueForFiniteType(literalExpr, valueType, data); - return valueType; - } - literalValueType = valueExpr.getBType(); - } - } - if (literalValueType != null) { - return literalValueType; - } + BType basicType; + BasicTypeBitSet basicTypeBitSet = widenToBasicTypes(expectedType.semType()); + if ((basicTypeBitSet.bitset & PredefinedType.FLOAT.bitset) != 0) { + basicType = symTable.floatType; + } else if ((basicTypeBitSet.bitset & PredefinedType.DECIMAL.bitset) != 0) { + basicType = symTable.decimalType; + } else { + return literalExpr.getBType(); } - return literalExpr.getBType(); + + if (literalAssignableToFiniteType(literalExpr, (BFiniteType) expectedType, basicType.tag)) { + BType valueType = setLiteralValueAndGetType(literalExpr, basicType, data); + setLiteralValueForFiniteType(literalExpr, valueType, data); + return valueType; + } + return basicType; } else if (expectedType.tag == TypeTags.UNION) { BUnionType unionType = (BUnionType) expectedType; for (int tag = TypeTags.FLOAT; tag <= TypeTags.DECIMAL; tag++) { @@ -828,7 +833,7 @@ public BType getTypeOfDecimalFloatingPointLiteral(BLangLiteral literalExpr, Obje ? symTable.floatType : symTable.semanticError; } - public BType getTypeOfHexFloatingPointLiteral(BLangLiteral literalExpr, Object literalValue, BType expType, + public BType getTypeOfHexFloatingPointLiteral(BLangNumericLiteral literalExpr, Object literalValue, BType expType, AnalyzerData data) { String numericLiteral = String.valueOf(literalValue); if (!types.validateFloatLiteral(literalExpr.pos, numericLiteral)) { @@ -858,19 +863,19 @@ public BType setLiteralValueAndGetType(BLangLiteral literalExpr, BType expType, BType expectedType = Types.getImpliedType(expType); if (literalExpr.getKind() == NodeKind.NUMERIC_LITERAL) { - NodeKind kind = ((BLangNumericLiteral) literalExpr).kind; - if (kind == NodeKind.INTEGER_LITERAL) { - return getIntegerLiteralType(literalExpr, literalValue, expectedType, data); - } else if (kind == NodeKind.DECIMAL_FLOATING_POINT_LITERAL) { - if (NumericLiteralSupport.isFloatDiscriminated(literalExpr.originalValue)) { - return getTypeOfLiteralWithFloatDiscriminator(literalExpr, literalValue, expectedType, data); - } else if (NumericLiteralSupport.isDecimalDiscriminated(literalExpr.originalValue)) { - return getTypeOfLiteralWithDecimalDiscriminator(literalExpr, literalValue, expectedType, data); + BLangNumericLiteral numericLiteral = (BLangNumericLiteral) literalExpr; + if (numericLiteral.kind == NodeKind.INTEGER_LITERAL) { + return getIntegerLiteralType(numericLiteral, literalValue, expectedType, data); + } else if (numericLiteral.kind == NodeKind.DECIMAL_FLOATING_POINT_LITERAL) { + if (NumericLiteralSupport.isFloatDiscriminated(numericLiteral.originalValue)) { + return getTypeOfLiteralWithFloatDiscriminator(numericLiteral, literalValue, expectedType, data); + } else if (NumericLiteralSupport.isDecimalDiscriminated(numericLiteral.originalValue)) { + return getTypeOfLiteralWithDecimalDiscriminator(numericLiteral, literalValue, expectedType, data); } else { - return getTypeOfDecimalFloatingPointLiteral(literalExpr, literalValue, expectedType, data); + return getTypeOfDecimalFloatingPointLiteral(numericLiteral, literalValue, expectedType, data); } } else { - return getTypeOfHexFloatingPointLiteral(literalExpr, literalValue, expectedType, data); + return getTypeOfHexFloatingPointLiteral(numericLiteral, literalValue, expectedType, data); } } @@ -928,9 +933,8 @@ public BType setLiteralValueAndGetType(BLangLiteral literalExpr, BType expType, if (referedType.tag == TypeTags.BYTE_ARRAY) { // check whether this is a byte array byte[] byteArray = types.convertToByteArray((String) literalExpr.value); - literalType = new BArrayType(symTable.byteType, null, byteArray.length, - BArrayState.CLOSED); - if (Symbols.isFlagOn(expectedType.flags, Flags.READONLY)) { + literalType = new BArrayType(typeEnv, symTable.byteType, null, byteArray.length, BArrayState.CLOSED); + if (Symbols.isFlagOn(expectedType.getFlags(), Flags.READONLY)) { literalType = ImmutableTypeCloner.getEffectiveImmutableType(literalExpr.pos, types, literalType, data.env, symTable, anonymousModelHelper, names); } @@ -938,7 +942,7 @@ public BType setLiteralValueAndGetType(BLangLiteral literalExpr, BType expType, if (expectedType.tag == TypeTags.ARRAY) { BArrayType arrayType = (BArrayType) expectedType; if (arrayType.state == BArrayState.INFERRED) { - arrayType.size = byteArray.length; + arrayType.setSize(byteArray.length); arrayType.state = BArrayState.CLOSED; } } @@ -969,7 +973,8 @@ private BType getTypeMatchingFloatOrDecimal(BType finiteType, List member } } if (finiteType.tag == TypeTags.FINITE) { - return checkIfOutOfRangeAndReturnType((BFiniteType) finiteType, literalExpr, literalExpr.value, data); + return checkIfOutOfRangeAndReturnType((BFiniteType) finiteType, (BLangNumericLiteral) literalExpr, + literalExpr.value, data); } return symTable.intType; } @@ -1000,15 +1005,9 @@ private BType getAndSetAssignableUnionMember(BLangLiteral literalExpr, BUnionTyp return symTable.noType; } - private boolean literalAssignableToFiniteType(BLangLiteral literalExpr, BFiniteType finiteType, - int targetMemberTypeTag) { - for (BLangExpression valueExpr : finiteType.getValueSpace()) { - if (Types.getImpliedType(valueExpr.getBType()).tag == targetMemberTypeTag && - types.checkLiteralAssignabilityBasedOnType((BLangLiteral) valueExpr, literalExpr)) { - return true; - } - } - return false; + private boolean literalAssignableToFiniteType(BLangNumericLiteral literalExpr, BFiniteType finiteType, + int targetTypeTag) { + return types.checkLiteralAssignabilityBasedOnType(literalExpr, finiteType, targetTypeTag); } public void setLiteralValueForFiniteType(BLangLiteral literalExpr, BType type, AnalyzerData data) { @@ -1018,6 +1017,9 @@ public void setLiteralValueForFiniteType(BLangLiteral literalExpr, BType type, A } private BType getFiniteTypeWithValuesOfSingleType(BUnionType unionType, BType matchType) { + assert matchType.tag == TypeTags.BYTE || matchType.tag == TypeTags.INT || + matchType.tag == TypeTags.FLOAT || matchType.tag == TypeTags.DECIMAL; + List finiteTypeMembers = types.getAllTypes(unionType, true).stream() .filter(memType -> Types.getImpliedType(memType).tag == TypeTags.FINITE) .map(memFiniteType -> (BFiniteType) memFiniteType) @@ -1027,24 +1029,20 @@ private BType getFiniteTypeWithValuesOfSingleType(BUnionType unionType, BType ma return symTable.semanticError; } - int tag = Types.getImpliedType(matchType).tag; - Set matchedValueSpace = new LinkedHashSet<>(); - + List newValueSpace = new ArrayList<>(); for (BFiniteType finiteType : finiteTypeMembers) { - Set set = new HashSet<>(); - for (BLangExpression expression : finiteType.getValueSpace()) { - if (Types.getImpliedType(expression.getBType()).tag == tag) { - set.add(expression); + for (SemNamedType semNamedType : finiteType.valueSpace) { + if (SemTypes.isSubtype(types.semTypeCtx, semNamedType.semType(), matchType.semType())) { + newValueSpace.add(semNamedType); } } - matchedValueSpace.addAll(set); } - if (matchedValueSpace.isEmpty()) { + if (newValueSpace.isEmpty()) { return symTable.semanticError; } - return new BFiniteType(null, matchedValueSpace); + return new BFiniteType(null, newValueSpace.toArray(SemNamedType[]::new)); } private BType getIntLiteralType(BType expType, Object literalValue, AnalyzerData data) { @@ -1155,7 +1153,7 @@ public void visit(BLangTableConstructorExpr tableConstructorExpr, AnalyzerData d recordLiteral.setBType(inherentMemberType); } } - BTableType tableType = new BTableType(TypeTags.TABLE, inherentMemberType, null); + BTableType tableType = new BTableType(typeEnv, inherentMemberType, null); if (!validateTableConstructorExpr(tableConstructorExpr, tableType, data)) { data.resultType = symTable.semanticError; return; @@ -1200,11 +1198,10 @@ public void visit(BLangTableConstructorExpr tableConstructorExpr, AnalyzerData d return; } - BTableType tableType = new BTableType(TypeTags.TABLE, inferTableMemberType(memTypes, applicableExpType), - null); + BTableType tableType = new BTableType(typeEnv, inferTableMemberType(memTypes, applicableExpType), null); - if (Symbols.isFlagOn(applicableExpType.flags, Flags.READONLY)) { - tableType.flags |= Flags.READONLY; + if (Symbols.isFlagOn(applicableExpType.getFlags(), Flags.READONLY)) { + tableType.addFlags(Flags.READONLY); } if (checkKeySpecifier(tableConstructorExpr, tableType, data)) { @@ -1240,7 +1237,7 @@ public void visit(BLangTableConstructorExpr tableConstructorExpr, AnalyzerData d BType resultType = checkExpr(clonedTableExpr, memType, data); if (resultType != symTable.semanticError && dlog.errorCount() == 0 && - isUniqueType(matchingTypes, resultType)) { + types.isUniqueType(matchingTypes, resultType)) { matchingTypes.add(resultType); } } @@ -1288,7 +1285,7 @@ private BType getInferredTableType(BLangTableConstructorExpr exprToLog, Analyzer } } - return new BTableType(TypeTags.TABLE, inferTableMemberType(memTypes, exprToLog, data), null); + return new BTableType(typeEnv, inferTableMemberType(memTypes, exprToLog, data), null); } private boolean checkKeySpecifier(BLangTableConstructorExpr tableConstructorExpr, BTableType tableType, @@ -1314,12 +1311,12 @@ private BType inferTableMemberType(List memTypes, BType expType) { result.add(memTypes.get(0)); - BUnionType unionType = BUnionType.create(null, result); + BUnionType unionType = BUnionType.create(typeEnv, null, result); for (int i = 1; i < memTypes.size(); i++) { BType source = memTypes.get(i); if (!types.isAssignable(source, unionType)) { result.add(source); - unionType = BUnionType.create(null, result); + unionType = BUnionType.create(typeEnv, null, result); } } @@ -1475,7 +1472,7 @@ private BRecordType createTableConstraintRecordType(Set inferredFields, recordSymbol.scope.define(field.name, field.symbol); } - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(typeEnv, recordSymbol); recordType.fields = inferredFields.stream().collect(getFieldCollector()); recordSymbol.type = recordType; @@ -1788,7 +1785,7 @@ private BType createTableKeyConstraint(List fieldNames, BType constraint return memTypes.get(0).type; } - return new BTupleType(memTypes); + return new BTupleType(typeEnv, memTypes); } protected BType checkListConstructorCompatibility(BType bType, BLangListConstructorExpr listConstructor, @@ -1826,7 +1823,7 @@ private BType checkListConstructorCompatibility(BType referredType, BType origin BType memCompatibiltyType = checkListConstructorCompatibility(listCompatibleMemType, listConstructor, data); if (memCompatibiltyType != symTable.semanticError && dlog.errorCount() == 0 && - isUniqueType(compatibleTypes, memCompatibiltyType)) { + types.isUniqueType(compatibleTypes, memCompatibiltyType)) { compatibleTypes.add(memCompatibiltyType); } } @@ -1899,14 +1896,14 @@ private BType checkListConstructorCompatibility(BType referredType, BType origin List members = new ArrayList<>(); inferredTupleDetails.fixedMemberTypes.forEach(memberType -> members.add(new BTupleMember(memberType, - new BVarSymbol(memberType.flags, null, null, memberType, null, null, null)))); - BTupleType tupleType = new BTupleType(members); + new BVarSymbol(memberType.getFlags(), null, null, memberType, null, null, null)))); + BTupleType tupleType = new BTupleType(typeEnv, members); if (!inferredTupleDetails.restMemberTypes.isEmpty()) { tupleType.restType = getRepresentativeBroadType(inferredTupleDetails.restMemberTypes); } listConstructor.typedescType = tupleType; - return new BTypedescType(listConstructor.typedescType, null); + return new BTypedescType(typeEnv, listConstructor.typedescType, null); } if (referredType == symTable.semanticError) { @@ -1951,7 +1948,7 @@ private void updateInferredTupleDetailsFromSpreadMember(Location spreadMemberPos } else if (spreadOpExprType.tag == TypeTags.ARRAY) { BArrayType bArrayType = (BArrayType) spreadOpExprType; if (bArrayType.state == BArrayState.CLOSED) { - for (int i = 0; i < bArrayType.size; i++) { + for (int i = 0; i < bArrayType.getSize(); i++) { BType memberType = bArrayType.eType; inferredTupleDetails.fixedMemberTypes.add(memberType); } @@ -1971,13 +1968,14 @@ private BType getListConstructorCompatibleNonUnionType(BType type, AnalyzerData TypeTags.TUPLE, TypeTags.READONLY, TypeTags.TYPEDESC -> type; - case TypeTags.JSON -> !Symbols.isFlagOn(referredType.flags, Flags.READONLY) ? symTable.arrayJsonType : + case TypeTags.JSON -> !Symbols.isFlagOn(referredType.getFlags(), Flags.READONLY) ? symTable.arrayJsonType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayJsonType, data.env, symTable, anonymousModelHelper, names); - case TypeTags.ANYDATA -> !Symbols.isFlagOn(referredType.flags, Flags.READONLY) ? symTable.arrayAnydataType : + case TypeTags.ANYDATA -> !Symbols.isFlagOn(referredType.getFlags(), Flags.READONLY) ? + symTable.arrayAnydataType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayAnydataType, data.env, symTable, anonymousModelHelper, names); - case TypeTags.ANY -> !Symbols.isFlagOn(referredType.flags, Flags.READONLY) ? symTable.arrayAllType : + case TypeTags.ANY -> !Symbols.isFlagOn(referredType.getFlags(), Flags.READONLY) ? symTable.arrayAllType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.arrayAllType, data.env, symTable, anonymousModelHelper, names); default -> symTable.semanticError; @@ -1999,7 +1997,7 @@ private BType checkArrayType(BLangListConstructorExpr listConstructor, BArrayTyp switch (spreadOpType.tag) { case TypeTags.ARRAY: - int arraySize = ((BArrayType) spreadOpType).size; + int arraySize = ((BArrayType) spreadOpType).getSize(); if (arraySize >= 0) { listExprSize += arraySize; continue; @@ -2025,11 +2023,12 @@ private BType checkArrayType(BLangListConstructorExpr listConstructor, BArrayTyp BType eType = arrayType.eType; if (arrayType.state == BArrayState.INFERRED) { - arrayType.size = listExprSize; + arrayType.setSize(listExprSize); arrayType.state = BArrayState.CLOSED; - } else if (arrayType.state != BArrayState.OPEN && arrayType.size != listExprSize) { - if (arrayType.size < listExprSize) { - dlog.error(listConstructor.pos, DiagnosticErrorCode.MISMATCHING_ARRAY_LITERAL_VALUES, arrayType.size, + } else if (arrayType.state != BArrayState.OPEN && arrayType.getSize() != listExprSize) { + if (arrayType.getSize() < listExprSize) { + dlog.error(listConstructor.pos, DiagnosticErrorCode.MISMATCHING_ARRAY_LITERAL_VALUES, + arrayType.getSize(), listExprSize); return symTable.semanticError; } @@ -2103,7 +2102,7 @@ private BType checkTupleType(BLangListConstructorExpr listConstructor, BTupleTyp switch (spreadOpType.tag) { case TypeTags.ARRAY: - int arraySize = ((BArrayType) spreadOpType).size; + int arraySize = ((BArrayType) spreadOpType).getSize(); if (arraySize >= 0) { listExprSize += arraySize; continue; @@ -2157,7 +2156,7 @@ private BType checkTupleType(BLangListConstructorExpr listConstructor, BTupleTyp BLangExpression spreadOpExpr = ((BLangListConstructorSpreadOpExpr) expr).expr; BType spreadOpType; if (restType != null && restType != symTable.noType && remainNonRestCount == 0) { - BType targetType = new BArrayType(restType); + BType targetType = new BArrayType(typeEnv, restType); BType possibleType = silentTypeCheckExpr(spreadOpExpr, targetType, data); if (possibleType == symTable.semanticError) { spreadOpType = checkExpr(spreadOpExpr, data); @@ -2173,7 +2172,7 @@ private BType checkTupleType(BLangListConstructorExpr listConstructor, BTupleTyp case TypeTags.ARRAY: BArrayType spreadOpArray = (BArrayType) spreadOpReferredType; if (spreadOpArray.state == BArrayState.CLOSED) { - for (int i = 0; i < spreadOpArray.size && nonRestTypeIndex < memberTypeSize; + for (int i = 0; i < spreadOpArray.getSize() && nonRestTypeIndex < memberTypeSize; i++, nonRestTypeIndex++) { if (types.typeIncompatible(spreadOpExpr.pos, spreadOpArray.eType, members.get(nonRestTypeIndex).type)) { @@ -2181,7 +2180,7 @@ private BType checkTupleType(BLangListConstructorExpr listConstructor, BTupleTyp } } - if (remainNonRestCount < spreadOpArray.size) { + if (remainNonRestCount < spreadOpArray.getSize()) { if (types.typeIncompatible(spreadOpExpr.pos, spreadOpArray.eType, restType)) { return symTable.semanticError; } @@ -2357,8 +2356,8 @@ protected BType getInferredTupleType(BLangListConstructorExpr listConstructor, B } List members = new ArrayList<>(); fixedMemberTypes.forEach(memberType -> members.add(new BTupleMember(memberType, - new BVarSymbol(memberType.flags, null, null, memberType, null, null, null)))); - BTupleType tupleType = new BTupleType(members); + new BVarSymbol(memberType.getFlags(), null, null, memberType, null, null, null)))); + BTupleType tupleType = new BTupleType(typeEnv, members); if (!restMemberTypes.isEmpty()) { tupleType.restType = getRepresentativeBroadType(restMemberTypes); } @@ -2367,11 +2366,10 @@ protected BType getInferredTupleType(BLangListConstructorExpr listConstructor, B return tupleType; } - tupleType.flags |= Flags.READONLY; + tupleType.addFlags(Flags.READONLY); return tupleType; } - @Override public void visit(BLangRecordLiteral recordLiteral, AnalyzerData data) { BType expType = data.expType; @@ -2393,7 +2391,7 @@ public BType getEffectiveMappingType(BLangRecordLiteral recordLiteral, BType app AnalyzerData data) { BType refType = Types.getImpliedType(applicableMappingType); if (applicableMappingType == symTable.semanticError || - (refType.tag == TypeTags.RECORD && Symbols.isFlagOn(applicableMappingType.flags, + (refType.tag == TypeTags.RECORD && Symbols.isFlagOn(applicableMappingType.getFlags(), Flags.READONLY))) { return applicableMappingType; } @@ -2472,7 +2470,7 @@ public BType getEffectiveMappingType(BLangRecordLiteral recordLiteral, BType app recordSymbol.scope.define(fieldName, fieldSymbol); } - BRecordType recordType = new BRecordType(recordSymbol, recordSymbol.flags); + BRecordType recordType = new BRecordType(typeEnv, recordSymbol, recordSymbol.flags); if (refType.tag == TypeTags.MAP) { recordType.sealed = false; recordType.restFieldType = ((BMapType) refType).constraint; @@ -2506,7 +2504,7 @@ public BType getEffectiveMappingType(BLangRecordLiteral recordLiteral, BType app recordType.restFieldType = applicableRecordType.restFieldType; if (recordType.sealed && allReadOnlyFields) { - recordType.flags |= Flags.READONLY; + recordType.addFlags(Flags.READONLY); recordType.tsymbol.flags |= Flags.READONLY; } @@ -2568,7 +2566,7 @@ public BType checkMappingConstructorCompatibility(BType bType, BLangRecordLitera mappingConstructor, data); if (memCompatibiltyType != symTable.semanticError && dlog.errorCount() == 0 && - isUniqueType(compatibleTypes, memCompatibiltyType)) { + types.isUniqueType(compatibleTypes, memCompatibiltyType)) { compatibleTypes.add(memCompatibiltyType); } } @@ -2658,15 +2656,15 @@ private BType getMappingConstructorCompatibleNonUnionType(BType type, AnalyzerDa case TypeTags.READONLY: return type; case TypeTags.JSON: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapJsonType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapJsonType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapJsonType, data.env, symTable, anonymousModelHelper, names); case TypeTags.ANYDATA: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapAnydataType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapAnydataType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapAnydataType, data.env, symTable, anonymousModelHelper, names); case TypeTags.ANY: - return !Symbols.isFlagOn(type.flags, Flags.READONLY) ? symTable.mapAllType : + return !Symbols.isFlagOn(type.getFlags(), Flags.READONLY) ? symTable.mapAllType : ImmutableTypeCloner.getEffectiveImmutableType(null, types, symTable.mapAllType, data.env, symTable, anonymousModelHelper, names); case TypeTags.INTERSECTION: @@ -2898,7 +2896,7 @@ public void visit(BLangWorkerFlushExpr workerFlushExpr, AnalyzerData data) { } } } - BType actualType = BUnionType.create(null, symTable.errorType, symTable.nilType); + BType actualType = BUnionType.create(typeEnv, null, symTable.errorType, symTable.nilType); data.resultType = types.checkType(workerFlushExpr, actualType, data.expType); } @@ -3199,11 +3197,10 @@ public void visit(BLangSimpleVarRef varRefExpr, AnalyzerData data) { if (symbol.kind == SymbolKind.TYPE_DEF) { BTypeDefinitionSymbol typeDefSym = (BTypeDefinitionSymbol) symbol; actualType = Types.getImpliedType(symbol.type).tag == TypeTags.TYPEDESC ? - typeDefSym.referenceType - : new BTypedescType(typeDefSym.referenceType, null); + typeDefSym.referenceType : new BTypedescType(typeEnv, typeDefSym.referenceType, null); } else { actualType = symbol.type.tag == TypeTags.TYPEDESC ? symbol.type - : new BTypedescType(symbol.type, null); + : new BTypedescType(typeEnv, symbol.type, null); } varRefExpr.symbol = symbol; } else if ((symbol.tag & SymTag.CONSTANT) == SymTag.CONSTANT) { @@ -3295,7 +3292,7 @@ public void visit(BLangRecordVarRef varRefExpr, AnalyzerData data) { return; } - BRecordType bRecordType = new BRecordType(recordSymbol); + BRecordType bRecordType = new BRecordType(typeEnv, recordSymbol); bRecordType.fields = fields; recordSymbol.type = bRecordType; varRefExpr.symbol = new BVarSymbol(0, recordSymbol.name, recordSymbol.getOriginalName(), @@ -3419,8 +3416,8 @@ public void visit(BLangErrorVarRef varRefExpr, AnalyzerData data) { BType errorDetailType = errorRefRestFieldType == symTable.anydataOrReadonly ? symTable.errorType.detailType - : new BMapType(TypeTags.MAP, errorRefRestFieldType, null, Flags.PUBLIC); - data.resultType = new BErrorType(symTable.errorType.tsymbol, errorDetailType); + : new BMapType(typeEnv, TypeTags.MAP, errorRefRestFieldType, null, Flags.PUBLIC); + data.resultType = new BErrorType(typeEnv, symTable.errorType.tsymbol, errorDetailType); } private void checkIndirectErrorVarRef(BLangErrorVarRef varRefExpr, AnalyzerData data) { @@ -3450,11 +3447,11 @@ public void visit(BLangTupleVarRef varRefExpr, AnalyzerData data) { for (int i = 0; i < varRefExpr.expressions.size(); i++) { ((BLangVariableReference) varRefExpr.expressions.get(i)).isLValue = true; BType memberType = checkExpr(varRefExpr.expressions.get(i), symTable.noType, data); - BVarSymbol varSymbol = new BVarSymbol(memberType.flags, null, null, memberType, null, + BVarSymbol varSymbol = new BVarSymbol(memberType.getFlags(), null, null, memberType, null, null, null); results.add(new BTupleMember(memberType, varSymbol)); } - BTupleType actualType = new BTupleType(results); + BTupleType actualType = new BTupleType(typeEnv, results); if (varRefExpr.restParam != null) { BLangExpression restExpr = varRefExpr.restParam; ((BLangVariableReference) restExpr).isLValue = true; @@ -3604,7 +3601,7 @@ private void checkFieldBasedAccess(BLangFieldBasedAccess fieldAccessExpr, boolea private boolean isAllReadonlyTypes(BType type) { type = Types.getImpliedType(type); if (type.tag != TypeTags.UNION) { - return Symbols.isFlagOn(type.flags, Flags.READONLY); + return Symbols.isFlagOn(type.getFlags(), Flags.READONLY); } for (BType memberType : ((BUnionType) type).getMemberTypes()) { @@ -3626,7 +3623,7 @@ private boolean isInitializationInInit(BType type, AnalyzerData data) { private boolean isInvalidReadonlyFieldUpdate(BType type, String fieldName) { if (Types.getImpliedType(type).tag == TypeTags.RECORD) { - if (Symbols.isFlagOn(type.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { return true; } @@ -3660,11 +3657,11 @@ private boolean isXmlAccess(BLangFieldBasedAccess fieldAccessExpr) { return true; } - if (expr.getKind() == NodeKind.FIELD_BASED_ACCESS_EXPR && hasLaxOriginalType((BLangFieldBasedAccess) expr) + if (expr.getKind() == NodeKind.FIELD_BASED_ACCESS_EXPR && hasLaxOriginalType((BLangFieldBasedAccess) expr) && exprType.tag == TypeTags.UNION) { - Set memberTypes = ((BUnionType) exprType).getMemberTypes(); - return memberTypes.contains(symTable.xmlType) || memberTypes.contains(symTable.xmlElementType); - } + SemType s = exprType.semType(); + return SemTypes.containsType(types.semTypeCtx, s, PredefinedType.XML_ELEMENT); + } return false; } @@ -3798,7 +3795,7 @@ public void visit(BLangErrorConstructorExpr errorConstructorExpr, AnalyzerData d if (errorDetailTypes.size() == 1) { detailCandidate = errorDetailTypes.get(0); } else { - detailCandidate = BUnionType.create(null, new LinkedHashSet<>(errorDetailTypes)); + detailCandidate = BUnionType.create(typeEnv, null, new LinkedHashSet<>(errorDetailTypes)); } BLangRecordLiteral recordLiteral = createRecordLiteralForErrorConstructor(errorConstructorExpr); @@ -4001,11 +3998,12 @@ private List expandExpectedErrorTypes(BType candidateType) { List expandedCandidates = new ArrayList<>(); if (referredType.tag == TypeTags.UNION) { for (BType memberType : ((BUnionType) referredType).getMemberTypes()) { - if (types.isAssignable(memberType, symTable.errorType)) { + if (memberType.tag != TypeTags.SEMANTIC_ERROR && types.isAssignable(memberType, symTable.errorType)) { expandedCandidates.add(memberType); } } - } else if (types.isAssignable(candidateType, symTable.errorType)) { + } else if (candidateType.tag != TypeTags.SEMANTIC_ERROR && + types.isAssignable(candidateType, symTable.errorType)) { expandedCandidates.add(candidateType); } @@ -4115,7 +4113,7 @@ private BTupleType getResourcePathType(List pathSegm pathSegmentCount--; } - BTupleType resourcePathType = new BTupleType(new ArrayList<>()); + BTupleType resourcePathType = new BTupleType(typeEnv, new ArrayList<>()); if (pathSegmentCount > 0 && lastPathSegmentSym.kind != SymbolKind.RESOURCE_ROOT_PATH_SEGMENT) { for (BResourcePathSegmentSymbol s : pathSegmentSymbols.subList(0, pathSegmentCount)) { BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(s.type); @@ -4142,7 +4140,8 @@ public boolean validateResourceAccessPathSegmentTypes(BLangListConstructorExpr r if (clonedPathSegment.getKind() == NodeKind.LIST_CONSTRUCTOR_SPREAD_OP) { BLangExpression spreadOpExpr = ((BLangListConstructorSpreadOpExpr) clonedPathSegment).expr; BType pathSegmentType = checkExpr(spreadOpExpr, data); - if (!types.isAssignable(pathSegmentType, new BArrayType(symTable.pathParamAllowedType))) { + if (!types.isAssignable(pathSegmentType, + new BArrayType(typeEnv, symTable.pathParamAllowedType))) { dlog.error(clonedPathSegment.getPosition(), DiagnosticErrorCode.UNSUPPORTED_RESOURCE_ACCESS_REST_SEGMENT_TYPE, pathSegmentType); isValidResourceAccessPathSegmentTypes = false; @@ -4256,7 +4255,7 @@ private void checkInLangLib(BLangInvocation iExpr, BType varRefType, AnalyzerDat private boolean checkInvalidImmutableValueUpdate(BLangInvocation iExpr, BType varRefType, BSymbol langLibMethodSymbol, AnalyzerData data) { - if (!Symbols.isFlagOn(varRefType.flags, Flags.READONLY)) { + if (!Symbols.isFlagOn(varRefType.getFlags(), Flags.READONLY)) { return false; } @@ -4283,33 +4282,13 @@ private boolean checkInvalidImmutableValueUpdate(BLangInvocation iExpr, BType va return true; } - private boolean isFixedLengthList(BType type) { - type = Types.getImpliedType(type); - switch(type.tag) { - case TypeTags.ARRAY: - return (((BArrayType) type).state != BArrayState.OPEN); - case TypeTags.TUPLE: - return (((BTupleType) type).restType == null); - case TypeTags.UNION: - BUnionType unionType = (BUnionType) type; - for (BType member : unionType.getMemberTypes()) { - if (!isFixedLengthList(member)) { - return false; - } - } - return true; - default: - return false; - } - } - private void checkIllegalStorageSizeChangeMethodCall(BLangInvocation iExpr, BType varRefType, AnalyzerData data) { String invocationName = iExpr.name.getValue(); if (!LIST_LENGTH_MODIFIER_FUNCTIONS.contains(invocationName)) { return; } - if (isFixedLengthList(varRefType)) { + if (types.isFixedLengthList(varRefType)) { dlog.error(iExpr.name.pos, DiagnosticErrorCode.ILLEGAL_FUNCTION_CHANGE_LIST_SIZE, invocationName, varRefType); data.resultType = symTable.semanticError; @@ -4420,9 +4399,9 @@ public void visit(BLangObjectConstructorExpression objectCtorExpression, Analyze classNode.oceEnvData.typeInit = objectCtorExpression.typeInit; dlog.unmute(); - if (Symbols.isFlagOn(data.expType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(data.expType.getFlags(), Flags.READONLY)) { handleObjectConstrExprForReadOnly(objectCtorExpression, actualObjectType, typeDefEnv, false, data); - } else if (!typeRefs.isEmpty() && Symbols.isFlagOn(typeRefs.get(0).getBType().flags, + } else if (!typeRefs.isEmpty() && Symbols.isFlagOn(typeRefs.get(0).getBType().getFlags(), Flags.READONLY)) { handleObjectConstrExprForReadOnly(objectCtorExpression, actualObjectType, typeDefEnv, true, data); } else { @@ -4677,7 +4656,7 @@ private BType checkObjectType(BType actualType, BLangTypeInit cIExpr, AnalyzerDa } private BUnionType createNextReturnType(Location pos, BStreamType streamType, AnalyzerData data) { - BRecordType recordType = new BRecordType(null, Flags.ANONYMOUS); + BRecordType recordType = new BRecordType(typeEnv, null, Flags.ANONYMOUS); recordType.restFieldType = symTable.noType; recordType.sealed = true; @@ -4698,7 +4677,7 @@ private BUnionType createNextReturnType(Location pos, BStreamType streamType, An retTypeMembers.add(recordType); retTypeMembers.addAll(types.getAllTypes(streamType.completionType, false)); - BUnionType unionType = BUnionType.create(null); + BUnionType unionType = BUnionType.create(typeEnv, null); unionType.addAll(retTypeMembers); unionType.tsymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, 0, Names.EMPTY, data.env.enclPkg.symbol.pkgID, unionType, data.env.scope.owner, pos, VIRTUAL); @@ -4728,7 +4707,7 @@ private BType getObjectConstructorReturnType(BType objType, BType initRetType, A retTypeMembers.addAll(((BUnionType) initRetType).getMemberTypes()); retTypeMembers.remove(symTable.nilType); - BUnionType unionType = BUnionType.create(null, retTypeMembers); + BUnionType unionType = BUnionType.create(typeEnv, null, retTypeMembers); unionType.tsymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, 0, Names.EMPTY, data.env.enclPkg.symbol.pkgID, unionType, data.env.scope.owner, symTable.builtinPos, VIRTUAL); @@ -4904,24 +4883,24 @@ private void setResultTypeForWaitForAllExpr(BLangWaitForAllExpr waitForAllExpr, checkTypesForMap(waitForAllExpr, ((BMapType) referredType).constraint, data); LinkedHashSet memberTypesForMap = collectWaitExprTypes(waitForAllExpr.keyValuePairs); if (memberTypesForMap.size() == 1) { - data.resultType = new BMapType(TypeTags.MAP, + data.resultType = new BMapType(typeEnv, TypeTags.MAP, memberTypesForMap.iterator().next(), symTable.mapType.tsymbol); break; } - BUnionType constraintTypeForMap = BUnionType.create(null, memberTypesForMap); - data.resultType = new BMapType(TypeTags.MAP, constraintTypeForMap, symTable.mapType.tsymbol); + BUnionType constraintTypeForMap = BUnionType.create(typeEnv, null, memberTypesForMap); + data.resultType = new BMapType(typeEnv, TypeTags.MAP, constraintTypeForMap, symTable.mapType.tsymbol); break; case TypeTags.NONE: case TypeTags.ANY: checkTypesForMap(waitForAllExpr, expType, data); LinkedHashSet memberTypes = collectWaitExprTypes(waitForAllExpr.keyValuePairs); if (memberTypes.size() == 1) { - data.resultType = - new BMapType(TypeTags.MAP, memberTypes.iterator().next(), symTable.mapType.tsymbol); + data.resultType = new BMapType(typeEnv, TypeTags.MAP, memberTypes.iterator().next(), + symTable.mapType.tsymbol); break; } - BUnionType constraintType = BUnionType.create(null, memberTypes); - data.resultType = new BMapType(TypeTags.MAP, constraintType, symTable.mapType.tsymbol); + BUnionType constraintType = BUnionType.create(typeEnv, null, memberTypes); + data.resultType = new BMapType(typeEnv, TypeTags.MAP, constraintType, symTable.mapType.tsymbol); break; default: dlog.error(waitForAllExpr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, expType, @@ -4933,7 +4912,7 @@ private void setResultTypeForWaitForAllExpr(BLangWaitForAllExpr waitForAllExpr, private BRecordType getWaitForAllExprReturnType(BLangWaitForAllExpr waitExpr, Location pos, AnalyzerData data) { - BRecordType retType = new BRecordType(null, Flags.ANONYMOUS); + BRecordType retType = new BRecordType(typeEnv, null, Flags.ANONYMOUS); List keyVals = waitExpr.keyValuePairs; for (BLangWaitForAllExpr.BLangWaitKeyValue keyVal : keyVals) { @@ -5062,7 +5041,7 @@ private void checkWaitKeyValExpr(BLangWaitForAllExpr.BLangWaitKeyValue keyVal, B } else { expr = keyVal.valueExpr; } - BFutureType futureType = new BFutureType(TypeTags.FUTURE, type, null); + BFutureType futureType = new BFutureType(typeEnv, type, null); checkExpr(expr, futureType, data); setEventualTypeForExpression(expr, type, data); } @@ -5089,14 +5068,14 @@ private void setEventualTypeForExpression(BLangExpression expression, return; } - BUnionType eventualType = BUnionType.create(null, currentType, symTable.errorType); + BUnionType eventualType = BUnionType.create(typeEnv, null, currentType, symTable.errorType); BType referredExpType = Types.getImpliedType(currentExpectedType); if (((referredExpType.tag != TypeTags.NONE) && (referredExpType.tag != TypeTags.NIL)) && !types.isAssignable(eventualType, currentExpectedType)) { dlog.error(expression.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPE_WAIT_FUTURE_EXPR, currentExpectedType, eventualType, expression); } - futureType.constraint = eventualType; + futureType.setConstraint(eventualType); } private void setEventualTypeForWaitExpression(BLangExpression expression, Location pos, AnalyzerData data) { @@ -5109,7 +5088,7 @@ private void setEventualTypeForWaitExpression(BLangExpression expression, Locati } BType currentExpectedType = ((BFutureType) data.expType).constraint; BType referredExpType = Types.getImpliedType(currentExpectedType); - BUnionType eventualType = BUnionType.create(null, data.resultType, symTable.errorType); + BUnionType eventualType = BUnionType.create(typeEnv, null, data.resultType, symTable.errorType); if ((referredExpType.tag == TypeTags.NONE) || (referredExpType.tag == TypeTags.NIL)) { data.resultType = eventualType; return; @@ -5124,7 +5103,7 @@ private void setEventualTypeForWaitExpression(BLangExpression expression, Locati BType referredResultType = Types.getImpliedType(data.resultType); if (referredResultType.tag == TypeTags.FUTURE) { - ((BFutureType) data.resultType).constraint = eventualType; + ((BFutureType) data.resultType).setConstraint(eventualType); } else { data.resultType = eventualType; } @@ -5146,7 +5125,7 @@ private void setEventualTypeForAlternateWaitExpression(BLangExpression expressio BType currentExpectedType = ((BFutureType) data.expType).constraint; BType referredExpType = Types.getImpliedType(currentExpectedType); - BUnionType eventualType = BUnionType.create(null, data.resultType, symTable.errorType); + BUnionType eventualType = BUnionType.create(typeEnv, null, data.resultType, symTable.errorType); if ((referredExpType.tag == TypeTags.NONE) || (referredExpType.tag == TypeTags.NIL)) { data.resultType = eventualType; return; @@ -5161,7 +5140,7 @@ private void setEventualTypeForAlternateWaitExpression(BLangExpression expressio BType referredResultType = Types.getImpliedType(data.resultType); if (referredResultType.tag == TypeTags.FUTURE) { - ((BFutureType) referredResultType).constraint = eventualType; + ((BFutureType) referredResultType).setConstraint(eventualType); } else { data.resultType = eventualType; } @@ -5236,12 +5215,12 @@ private BType getConditionalExprType(BType lhsType, BType rhsType) { if (types.isAssignable(lhsType, rhsType)) { return rhsType; } - return BUnionType.create(null, lhsType, rhsType); + return BUnionType.create(typeEnv, null, lhsType, rhsType); } @Override public void visit(BLangWaitExpr waitExpr, AnalyzerData data) { - data.expType = new BFutureType(TypeTags.FUTURE, data.expType, null); + data.expType = new BFutureType(typeEnv, data.expType, null); checkExpr(waitExpr.getExpression(), data.expType, data); // Handle union types in lhs BType referredResultType = Types.getImpliedType(data.resultType); @@ -5251,7 +5230,7 @@ public void visit(BLangWaitExpr waitExpr, AnalyzerData data) { if (memberTypes.size() == 1) { data.resultType = memberTypes.toArray(new BType[0])[0]; } else { - data.resultType = BUnionType.create(null, memberTypes); + data.resultType = BUnionType.create(typeEnv, null, memberTypes); } } else if (data.resultType != symTable.semanticError) { // Handle other types except for semantic errors @@ -5312,7 +5291,7 @@ public void visit(BLangTrapExpr trapExpr, AnalyzerData data) { resultTypes.add(exprType); } resultTypes.add(symTable.errorType); - actualType = BUnionType.create(null, resultTypes); + actualType = BUnionType.create(typeEnv, null, resultTypes); } data.resultType = types.checkType(trapExpr, actualType, data.expType); @@ -5333,7 +5312,7 @@ public void visit(BLangBinaryExpr binaryExpr, AnalyzerData data) { data.resultType = symTable.semanticError; return; } - data.resultType = BUnionType.create(null, lhsResultType, rhsResultType); + data.resultType = BUnionType.create(typeEnv, null, lhsResultType, rhsResultType); return; } @@ -5375,7 +5354,8 @@ public void visit(BLangBinaryExpr binaryExpr, AnalyzerData data) { BType rightConstituent = getXMLConstituents(rhsType); if (leftConstituent != null && rightConstituent != null) { - actualType = new BXMLType(BUnionType.create(null, leftConstituent, rightConstituent), null); + actualType = + new BXMLType(BUnionType.create(typeEnv, null, leftConstituent, rightConstituent), null); break; } else if (leftConstituent != null || rightConstituent != null) { if (leftConstituent != null && types.isAssignable(rhsType, symTable.stringType)) { @@ -5447,23 +5427,19 @@ private BType getXmlStringBinaryOpResultType(BType opType, BType constituentType BTypeSymbol typeSymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, 0, Names.EMPTY, env.enclPkg.symbol.pkgID, null, env.scope.owner, pos, VIRTUAL); - BType type = new BXMLType(BUnionType.create(typeSymbol, constituentType, symTable.xmlTextType), null); + BType type = + new BXMLType(BUnionType.create(typeEnv, typeSymbol, constituentType, symTable.xmlTextType), null); typeSymbol.type = type; return type; } public boolean isOptionalFloatOrDecimal(BType expectedType) { - if (expectedType.tag == TypeTags.UNION && expectedType.isNullable() && expectedType.tag != TypeTags.ANY) { - Iterator memberTypeIterator = ((BUnionType) expectedType).getMemberTypes().iterator(); - while (memberTypeIterator.hasNext()) { - BType memberType = Types.getImpliedType(memberTypeIterator.next()); - if (memberType.tag == TypeTags.FLOAT || memberType.tag == TypeTags.DECIMAL) { - return true; - } - } - + if (!expectedType.isNullable()) { + return false; } - return false; + + SemType t = Core.diff(expectedType.semType(), PredefinedType.NIL); + return PredefinedType.FLOAT.equals(t) || PredefinedType.DECIMAL.equals(t); } private BType checkAndGetType(BLangExpression expr, SymbolEnv env, BLangBinaryExpr binaryExpr, AnalyzerData data) { @@ -5495,7 +5471,7 @@ public void visit(BLangTransactionalExpr transactionalExpr, AnalyzerData data) { @Override public void visit(BLangCommitExpr commitExpr, AnalyzerData data) { - BType actualType = BUnionType.create(null, symTable.errorType, symTable.nilType); + BType actualType = BUnionType.create(typeEnv, null, symTable.errorType, symTable.nilType); data.resultType = types.checkType(commitExpr, actualType, data.expType); } @@ -5544,14 +5520,14 @@ public void visit(BLangTypedescExpr accessExpr, AnalyzerData data) { int resolveTypeTag = Types.getImpliedType(accessExpr.resolvedType).tag; final BType actualType; if (resolveTypeTag != TypeTags.TYPEDESC && resolveTypeTag != TypeTags.NONE) { - actualType = new BTypedescType(accessExpr.resolvedType, null); + actualType = new BTypedescType(typeEnv, accessExpr.resolvedType, null); } else { actualType = accessExpr.resolvedType; } data.resultType = types.checkType(accessExpr, actualType, data.expType); } - public LinkedHashSet getBasicNumericTypes(LinkedHashSet memberTypes) { + public LinkedHashSet getBasicNumericTypes(Set memberTypes) { LinkedHashSet basicNumericTypes = new LinkedHashSet<>(memberTypes.size()); for (BType value : memberTypes) { @@ -5567,8 +5543,7 @@ public LinkedHashSet getBasicNumericTypes(LinkedHashSet memberType basicNumericTypes.add(symTable.decimalType); break; } else if (typeTag == TypeTags.FINITE) { - LinkedHashSet typesInValueSpace = getTypesInFiniteValueSpace((BFiniteType) referredType); - basicNumericTypes.addAll(getBasicNumericTypes(typesInValueSpace)); + basicNumericTypes.addAll(SemTypeHelper.broadTypes((BFiniteType) referredType, symTable)); } } return basicNumericTypes; @@ -5579,29 +5554,20 @@ public BType createFiniteTypeForNumericUnaryExpr(BLangUnaryExpr unaryExpr, Analy BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, 0, Names.EMPTY, data.env.enclPkg.symbol.pkgID, null, data.env.scope.owner, unaryExpr.pos, SOURCE); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - finiteType.addValue(newNumericLiteral); + BFiniteType finiteType = BFiniteType.newSingletonBFiniteType(finiteTypeSymbol, + SemTypeHelper.resolveSingletonType(newNumericLiteral)); finiteTypeSymbol.type = finiteType; types.setImplicitCastExpr(unaryExpr, unaryExpr.expr.getBType(), data.expType); return finiteType; } - public LinkedHashSet getTypesInFiniteValueSpace(BFiniteType referredType) { - Set valueSpace = referredType.getValueSpace(); - LinkedHashSet typesInValueSpace = new LinkedHashSet<>(valueSpace.size()); - for (BLangExpression expr : valueSpace) { - typesInValueSpace.add(expr.getBType()); - } - return typesInValueSpace; - } - - public BType getNewExpectedTypeForFiniteAndUnion(LinkedHashSet numericTypes, BType newExpectedType) { + public BType getNewExpectedTypeForFiniteAndUnion(Set numericTypes, BType newExpectedType) { LinkedHashSet basicNumericTypes = getBasicNumericTypes(numericTypes); if (basicNumericTypes.size() == 1) { newExpectedType = basicNumericTypes.iterator().next(); } else if (basicNumericTypes.size() > 1) { - newExpectedType = BUnionType.create(null, basicNumericTypes); + newExpectedType = BUnionType.create(typeEnv, null, basicNumericTypes); } return newExpectedType; } @@ -5614,22 +5580,24 @@ public BType setExpectedTypeForSubtractionOperator(AnalyzerData data) { if (TypeTags.isIntegerTypeTag(referredTypeTag)) { newExpectedType = types.getTypeIntersection(Types.IntersectionContext.compilerInternalIntersectionTestContext(), - BUnionType.create(null, symTable.intType, symTable.floatType, symTable.decimalType), + BUnionType.create(typeEnv, null, symTable.intType, symTable.floatType, + symTable.decimalType), symTable.intType, data.env); } else if (referredTypeTag == TypeTags.FLOAT || referredTypeTag == TypeTags.DECIMAL) { newExpectedType = types.getTypeIntersection(Types.IntersectionContext.compilerInternalIntersectionTestContext(), - BUnionType.create(null, symTable.intType, symTable.floatType, symTable.decimalType), + BUnionType.create(typeEnv, null, symTable.intType, symTable.floatType, + symTable.decimalType), referredType, data.env); } else if (referredTypeTag == TypeTags.FINITE) { - LinkedHashSet typesInValueSpace = getTypesInFiniteValueSpace((BFiniteType) referredType); + Set typesInValueSpace = SemTypeHelper.broadTypes((BFiniteType) referredType, symTable); newExpectedType = getNewExpectedTypeForFiniteAndUnion(typesInValueSpace, newExpectedType); } else if (referredTypeTag == TypeTags.UNION) { newExpectedType = getNewExpectedTypeForFiniteAndUnion(((BUnionType) referredType).getMemberTypes(), newExpectedType); } else if (referredTypeTag == TypeTags.JSON || referredTypeTag == TypeTags.ANYDATA || referredTypeTag == TypeTags.ANY) { - newExpectedType = BUnionType.create(null, symTable.intType, symTable.floatType, + newExpectedType = BUnionType.create(typeEnv, null, symTable.intType, symTable.floatType, symTable.decimalType); } return newExpectedType; @@ -5687,7 +5655,7 @@ public BType getActualTypeForOtherUnaryExpr(BLangUnaryExpr unaryExpr, AnalyzerDa // basic type (int) when checking the expression. LinkedHashSet intTypesInUnion = getIntSubtypesInUnionType((BUnionType) referredType); if (!intTypesInUnion.isEmpty()) { - BType newReferredType = BUnionType.create(null, intTypesInUnion); + BType newReferredType = BUnionType.create(typeEnv, null, intTypesInUnion); BType tempActualType = checkCompatibilityWithConstructedNumericLiteral(unaryExpr, newReferredType, data); if (tempActualType != symTable.semanticError) { @@ -5782,7 +5750,7 @@ public void visit(BLangUnaryExpr unaryExpr, AnalyzerData data) { } else if (OperatorKind.TYPEOF.equals(unaryExpr.operator)) { exprType = checkExpr(unaryExpr.expr, data); if (exprType != symTable.semanticError) { - actualType = new BTypedescType(exprType, null); + actualType = new BTypedescType(typeEnv, exprType, null); } } else { actualType = getActualTypeForOtherUnaryExpr(unaryExpr, data); @@ -5847,7 +5815,7 @@ public void visit(BLangTypeConversionExpr conversionExpr, AnalyzerData data) { } BType exprType = expr.getBType(); - if (types.isTypeCastable(expr, exprType, targetType, data.env)) { + if (types.isTypeCastable(exprType, targetType)) { // We reach this block only if the cast is valid, so we set the target type as the actual type. actualType = targetType; } else if (exprType != symTable.semanticError && exprType != symTable.noType) { @@ -5908,7 +5876,7 @@ public void visit(BLangArrowFunction bLangArrowFunction, AnalyzerData data) { expectedType = invokableType; } } - if (expectedType.tag != TypeTags.INVOKABLE || Symbols.isFlagOn(expectedType.flags, Flags.ANY_FUNCTION)) { + if (expectedType.tag != TypeTags.INVOKABLE || Symbols.isFlagOn(expectedType.getFlags(), Flags.ANY_FUNCTION)) { dlog.error(bLangArrowFunction.pos, DiagnosticErrorCode.ARROW_EXPRESSION_CANNOT_INFER_TYPE_FROM_LHS); data.resultType = symTable.semanticError; @@ -6113,7 +6081,7 @@ public void visit(BLangXMLElementLiteral bLangXMLElementLiteral, AnalyzerData da data.resultType = checkXmlSubTypeLiteralCompatibility(bLangXMLElementLiteral.pos, symTable.xmlElementType, data.expType, data); - if (Symbols.isFlagOn(data.resultType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(data.resultType.getFlags(), Flags.READONLY)) { markChildrenAsImmutable(bLangXMLElementLiteral, data); } } @@ -6330,8 +6298,8 @@ private boolean evaluateRawTemplateExprs(List exprs, if (listType.tag == TypeTags.ARRAY) { BArrayType arrayType = (BArrayType) listType; - if (arrayType.state == BArrayState.CLOSED && (exprs.size() != arrayType.size)) { - dlog.error(pos, code, arrayType.size, exprs.size()); + if (arrayType.state == BArrayState.CLOSED && (exprs.size() != arrayType.getSize())) { + dlog.error(pos, code, arrayType.getSize(), exprs.size()); return false; } @@ -6369,16 +6337,7 @@ private boolean evaluateRawTemplateExprs(List exprs, } private boolean containsAnyType(BType bType) { - BType type = Types.getImpliedType(bType); - if (type == symTable.anyType) { - return true; - } - - if (type.tag == TypeTags.UNION) { - return ((BUnionType) type).getMemberTypes().contains(symTable.anyType); - } - - return false; + return SemTypeHelper.containsType(types.semTypeCtx, bType, PredefinedType.ANY); } private BType getCompatibleRawTemplateType(BType bType, Location pos) { @@ -6476,7 +6435,7 @@ protected void visitCheckAndCheckPanicExpr(BLangCheckedExpr checkedExpr, Analyze } else { BType exprType = getCandidateType(checkedExpr, data.expType, data); if (exprType == symTable.semanticError) { - checkExprCandidateType = BUnionType.create(null, data.expType, symTable.errorType); + checkExprCandidateType = BUnionType.create(typeEnv, null, data.expType, symTable.errorType); } else { checkExprCandidateType = addDefaultErrorIfNoErrorComponentFound(data.expType); } @@ -6498,7 +6457,8 @@ protected void visitCheckAndCheckPanicExpr(BLangCheckedExpr checkedExpr, Analyze } } - boolean isErrorType = types.isAssignable(exprType, symTable.errorType); + boolean isErrorType = exprType.tag != TypeTags.SEMANTIC_ERROR && + types.isAssignable(exprType, symTable.errorType); BType referredExprType = Types.getImpliedType(exprType); if (referredExprType.tag != TypeTags.UNION && !isErrorType) { if (referredExprType.tag == TypeTags.READONLY) { @@ -6566,7 +6526,7 @@ protected void visitCheckAndCheckPanicExpr(BLangCheckedExpr checkedExpr, Analyze } else if (nonErrorTypes.size() == 1) { actualType = nonErrorTypes.get(0); } else { - actualType = BUnionType.create(null, new LinkedHashSet<>(nonErrorTypes)); + actualType = BUnionType.create(typeEnv, null, new LinkedHashSet<>(nonErrorTypes)); } data.resultType = types.checkType(checkedExpr, actualType, data.expType); @@ -6582,12 +6542,12 @@ private void rewriteWithEnsureTypeFunc(BLangCheckedExpr checkedExpr, BType type, if (rhsType == symTable.semanticError) { rhsType = getCandidateType(checkedExpr, rhsType, data); } - BType candidateLaxType = getCandidateLaxType(checkedExpr.expr, rhsType); + SemType candidateLaxType = getCandidateLaxType(checkedExpr.expr, rhsType); if (!types.isLaxFieldAccessAllowed(candidateLaxType)) { return; } ArrayList argExprs = new ArrayList<>(); - BType typedescType = new BTypedescType(data.expType, null); + BType typedescType = new BTypedescType(typeEnv, data.expType, null); BLangTypedescExpr typedescExpr = new BLangTypedescExpr(); typedescExpr.resolvedType = data.expType; typedescExpr.setBType(typedescType); @@ -6599,11 +6559,12 @@ private void rewriteWithEnsureTypeFunc(BLangCheckedExpr checkedExpr, BType type, checkedExpr.expr = invocation; } - private BType getCandidateLaxType(BLangNode expr, BType rhsType) { + private SemType getCandidateLaxType(BLangNode expr, BType rhsType) { + SemType t = rhsType.semType(); if (expr.getKind() == NodeKind.FIELD_BASED_ACCESS_EXPR) { - return types.getSafeType(rhsType, false, true); + return types.getErrorLiftType(t); } - return rhsType; + return t; } private BType getCandidateType(BLangCheckedExpr checkedExpr, BType checkExprCandidateType, AnalyzerData data) { @@ -6635,7 +6596,7 @@ private BType addDefaultErrorIfNoErrorComponentFound(BType type) { return type; } } - return BUnionType.create(null, type, symTable.errorType); + return BUnionType.create(typeEnv, null, type, symTable.errorType); } @Override @@ -6667,7 +6628,7 @@ public void visit(BLangAnnotAccessExpr annotAccessExpr, AnalyzerData data) { annotAccessExpr.annotationSymbol = (BAnnotationSymbol) symbol; BType annotType = ((BAnnotationSymbol) symbol).attachedType == null ? symTable.trueType : ((BAnnotationSymbol) symbol).attachedType; - actualType = BUnionType.create(null, annotType, symTable.nilType); + actualType = BUnionType.create(typeEnv, null, annotType, symTable.nilType); } data.resultType = this.types.checkType(annotAccessExpr, actualType, data.expType); @@ -6729,7 +6690,7 @@ private BType getEffectiveReadOnlyType(Location pos, BType type, AnalyzerData da return type; } - BUnionType nonReadOnlyUnion = BUnionType.create(null, nonReadOnlyTypes); + BUnionType nonReadOnlyUnion = BUnionType.create(typeEnv, null, nonReadOnlyTypes); nonReadOnlyUnion.add(ImmutableTypeCloner.getImmutableIntersectionType(pos, types, data.expType, data.env, symTable, anonymousModelHelper, names, new HashSet<>())); @@ -7146,7 +7107,7 @@ private void checkActionInvocation(BLangInvocation.BLangActionInvocation aInv, B return; } if (Symbols.isFlagOn(remoteFuncSymbol.flags, Flags.REMOTE) && - Symbols.isFlagOn(expType.flags, Flags.CLIENT) && + Symbols.isFlagOn(expType.getFlags(), Flags.CLIENT) && types.isNeverTypeOrStructureTypeWithARequiredNeverMember ((BType) ((InvokableSymbol) remoteFuncSymbol).getReturnType())) { dlog.error(aInv.pos, DiagnosticErrorCode.INVALID_CLIENT_REMOTE_METHOD_CALL); @@ -7235,7 +7196,7 @@ private BVarSymbol checkForIncRecordParamAllowAdditionalFields(BInvokableSymbol } private BType checkInvocationParam(BLangInvocation iExpr, AnalyzerData data) { - if (Symbols.isFlagOn(iExpr.symbol.type.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(iExpr.symbol.type.getFlags(), Flags.ANY_FUNCTION)) { dlog.error(iExpr.pos, DiagnosticErrorCode.INVALID_FUNCTION_POINTER_INVOCATION_WITH_TYPE); return symTable.semanticError; } @@ -7487,7 +7448,7 @@ private BType checkInvocationArgs(BLangInvocation iExpr, List paramTypes, PackageID pkgID = data.env.enclPkg.symbol.pkgID; List tupleMembers = new ArrayList<>(); BRecordTypeSymbol recordSymbol = createRecordTypeSymbol(pkgID, null, VIRTUAL, data); - mappingTypeRestArg = new BRecordType(recordSymbol); + mappingTypeRestArg = new BRecordType(typeEnv, recordSymbol); LinkedHashMap fields = new LinkedHashMap<>(); BType tupleRestType = null; BVarSymbol fieldSymbol; @@ -7519,7 +7480,7 @@ private BType checkInvocationArgs(BLangInvocation iExpr, List paramTypes, } } - BTupleType tupleType = new BTupleType(tupleMembers); + BTupleType tupleType = new BTupleType(typeEnv, tupleMembers); tupleType.restType = tupleRestType; listTypeRestArg = tupleType; referredListTypeRestArg = tupleType; @@ -7555,7 +7516,7 @@ private BType checkInvocationArgs(BLangInvocation iExpr, List paramTypes, LinkedHashSet restTypes = new LinkedHashSet<>(); restTypes.add(listTypeRestArg); restTypes.add(mappingTypeRestArg); - BType actualType = BUnionType.create(null, restTypes); + BType actualType = BUnionType.create(typeEnv, null, restTypes); checkTypeParamExpr(vararg, actualType, iExpr.langLibInvocation, data); } else { checkTypeParamExpr(vararg, listTypeRestArg, iExpr.langLibInvocation, data); @@ -7597,8 +7558,8 @@ private BType checkInvocationArgs(BLangInvocation iExpr, List paramTypes, long invokableSymbolFlags = invokableSymbol.flags; if (restType != symTable.semanticError && (Symbols.isFlagOn(invokableSymbolFlags, Flags.INTERFACE) || Symbols.isFlagOn(invokableSymbolFlags, Flags.NATIVE)) && - Symbols.isFlagOn(retType.flags, Flags.PARAMETERIZED)) { - retType = unifier.build(retType, data.expType, iExpr, types, symTable, dlog); + Symbols.isFlagOn(retType.getFlags(), Flags.PARAMETERIZED)) { + retType = unifier.build(typeEnv, retType, data.expType, iExpr, types, symTable, dlog); } // check argument types in arr:sort function @@ -7667,7 +7628,7 @@ private void checkArrayLibSortFuncArgs(BLangInvocation iExpr) { BLangExpression arrExpr = argExprs.get(0); BType arrType = arrExpr.getBType(); - boolean isOrderedType = types.isOrderedType(arrType, false); + boolean isOrderedType = types.isOrderedType(arrType); if (keyFunction == null) { if (!isOrderedType) { dlog.error(arrExpr.pos, DiagnosticErrorCode.INVALID_SORT_ARRAY_MEMBER_TYPE, arrType); @@ -7707,7 +7668,7 @@ private void checkArrayLibSortFuncArgs(BLangInvocation iExpr) { returnType = keyLambdaFunction.function.getBType().getReturnType(); } - if (!types.isOrderedType(returnType, false)) { + if (!types.isOrderedType(returnType)) { dlog.error(pos, DiagnosticErrorCode.INVALID_SORT_FUNC_RETURN_TYPE, returnType); } } @@ -7741,7 +7702,7 @@ private BVarSymbol checkParameterNameForDefaultArgument(BLangIdentifier argName, private BFutureType generateFutureType(BInvokableSymbol invocableSymbol, BType retType) { boolean isWorkerStart = Symbols.isFlagOn(invocableSymbol.flags, Flags.WORKER); - return new BFutureType(TypeTags.FUTURE, retType, null, isWorkerStart); + return new BFutureType(typeEnv, retType, null, isWorkerStart); } protected void checkTypeParamExpr(BLangExpression arg, BType expectedType, AnalyzerData data) { @@ -8029,7 +7990,7 @@ private TypeSymbolPair checkRecordLiteralKeyExpr(BLangExpression keyExpr, boolea fieldTypes.add(recordType.restFieldType); } - return new TypeSymbolPair(null, BUnionType.create(null, fieldTypes)); + return new TypeSymbolPair(null, BUnionType.create(typeEnv, null, fieldTypes)); } else if (keyExpr.getKind() == NodeKind.SIMPLE_VARIABLE_REF) { BLangSimpleVarRef varRef = (BLangSimpleVarRef) keyExpr; fieldName = names.fromIdNode(varRef.variableName); @@ -8075,7 +8036,7 @@ private BType getAllFieldType(BRecordType recordType) { possibleTypes.add(restFieldType); } - return BUnionType.create(null, possibleTypes); + return BUnionType.create(typeEnv, null, possibleTypes); } private boolean checkValidJsonOrMapLiteralKeyExpr(BLangExpression keyExpr, boolean computedKey, AnalyzerData data) { @@ -8162,12 +8123,12 @@ private BType checkObjectFieldAccess(BLangFieldBasedAccess bLangFieldBasedAccess return symTable.semanticError; } - if (Symbols.isFlagOn(fieldSymbol.type.flags, Flags.ISOLATED) && - !Symbols.isFlagOn(objectType.flags, Flags.ISOLATED)) { - fieldSymbol = ASTBuilderUtil.duplicateInvokableSymbol((BInvokableSymbol) fieldSymbol); + if (Symbols.isFlagOn(fieldSymbol.type.getFlags(), Flags.ISOLATED) && + !Symbols.isFlagOn(objectType.getFlags(), Flags.ISOLATED)) { + fieldSymbol = ASTBuilderUtil.duplicateInvokableSymbol(typeEnv, (BInvokableSymbol) fieldSymbol); fieldSymbol.flags &= ~Flags.ISOLATED; - fieldSymbol.type.flags &= ~Flags.ISOLATED; + fieldSymbol.type.setFlags(fieldSymbol.type.getFlags() & ~Flags.ISOLATED); } // Setting the field symbol. This is used during the code generation phase @@ -8221,7 +8182,7 @@ private void checkStringTemplateExprs(List exprs, Ana if (!types.isNonNilSimpleBasicTypeOrString(type)) { dlog.error(expr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, - BUnionType.create(null, symTable.intType, symTable.floatType, + BUnionType.create(typeEnv, null, symTable.intType, symTable.floatType, symTable.decimalType, symTable.stringType, symTable.booleanType), type); } @@ -8278,7 +8239,7 @@ private List concatSimilarKindXMLNodes(List ex !TypeTags.isIntegerTypeTag(referredType.tag) && !TypeTags.isStringTypeTag(referredType.tag)) { if (referredType != symTable.semanticError && !TypeTags.isXMLTypeTag(referredType.tag)) { dlog.error(expr.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, - BUnionType.create(null, symTable.intType, symTable.floatType, + BUnionType.create(typeEnv, null, symTable.intType, symTable.floatType, symTable.decimalType, symTable.stringType, symTable.booleanType, symTable.xmlType), type); } @@ -8355,7 +8316,7 @@ private BType checkObjectFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, return fieldTypeMembers.iterator().next(); } - return BUnionType.create(null, fieldTypeMembers); + return BUnionType.create(typeEnv, null, fieldTypeMembers); } private BType checkRecordFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, BType type, Name fieldName, @@ -8402,7 +8363,7 @@ private BType checkRecordFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, return fieldTypeMembers.iterator().next(); } - return BUnionType.create(null, fieldTypeMembers); + return BUnionType.create(typeEnv, null, fieldTypeMembers); } private boolean isFieldOptionalInRecords(BUnionType unionType, Name fieldName, @@ -8452,7 +8413,7 @@ private BType checkRecordFieldAccessLhsExpr(BLangFieldBasedAccess fieldAccessExp return fieldTypeMembers.iterator().next(); } - return BUnionType.create(null, fieldTypeMembers); + return BUnionType.create(typeEnv, null, fieldTypeMembers); } private BType checkOptionalRecordFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, BType varRefType, @@ -8499,7 +8460,7 @@ private BType checkOptionalRecordFieldAccessExpr(BLangFieldBasedAccess fieldAcce if (fieldTypeMembers.size() == 1) { fieldType = fieldTypeMembers.iterator().next(); } else { - fieldType = BUnionType.create(null, fieldTypeMembers); + fieldType = BUnionType.create(typeEnv, null, fieldTypeMembers); } return nonMatchedRecordExists ? types.addNilForNillableAccessType(fieldType) : fieldType; @@ -8623,7 +8584,7 @@ private BType checkFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, BType resolveXMLNamespace((BLangFieldBasedAccess.BLangPrefixedFieldBasedAccess) fieldAccessExpr, data); } BType laxFieldAccessType = getLaxFieldAccessType(varRefType); - actualType = BUnionType.create(null, laxFieldAccessType, symTable.errorType); + actualType = BUnionType.create(typeEnv, null, laxFieldAccessType, symTable.errorType); fieldAccessExpr.originalType = laxFieldAccessType; } else if (fieldAccessExpr.expr.getKind() == NodeKind.FIELD_BASED_ACCESS_EXPR && hasLaxOriginalType(((BLangFieldBasedAccess) fieldAccessExpr.expr))) { @@ -8632,7 +8593,7 @@ private BType checkFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr, BType if (fieldAccessExpr.fieldKind == FieldKind.WITH_NS) { resolveXMLNamespace((BLangFieldBasedAccess.BLangPrefixedFieldBasedAccess) fieldAccessExpr, data); } - actualType = BUnionType.create(null, laxFieldAccessType, symTable.errorType); + actualType = BUnionType.create(typeEnv, null, laxFieldAccessType, symTable.errorType); fieldAccessExpr.errorSafeNavigation = true; fieldAccessExpr.originalType = laxFieldAccessType; } else if (TypeTags.isXMLTypeTag(referredVarRefType.tag)) { @@ -8688,12 +8649,13 @@ private BType getLaxFieldAccessType(BType exprType) { return ((BMapType) exprType).constraint; case TypeTags.UNION: BUnionType unionType = (BUnionType) exprType; - if (types.isSameType(symTable.jsonType, unionType)) { + if (types.isSameType(Core.createJson(types.semTypeCtx), unionType.semType())) { return symTable.jsonType; } LinkedHashSet memberTypes = new LinkedHashSet<>(); unionType.getMemberTypes().forEach(bType -> memberTypes.add(getLaxFieldAccessType(bType))); - return memberTypes.size() == 1 ? memberTypes.iterator().next() : BUnionType.create(null, memberTypes); + return memberTypes.size() == 1 ? memberTypes.iterator().next() : + BUnionType.create(typeEnv, null, memberTypes); } return symTable.semanticError; } @@ -8718,7 +8680,7 @@ private BType checkOptionalFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr } referredType = nilRemovedSet.size() == 1 ? nilRemovedSet.iterator().next() : - BUnionType.create(null, nilRemovedSet); + BUnionType.create(typeEnv, null, nilRemovedSet); } } @@ -8735,7 +8697,8 @@ private BType checkOptionalFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr } else if (types.isLaxFieldAccessAllowed(referredType)) { BType laxFieldAccessType = getLaxFieldAccessType(referredType); actualType = accessCouldResultInError(referredType) ? - BUnionType.create(null, laxFieldAccessType, symTable.errorType) : laxFieldAccessType; + BUnionType.create(typeEnv, null, laxFieldAccessType, symTable.errorType) : + laxFieldAccessType; if (fieldAccessExpr.fieldKind == FieldKind.WITH_NS) { resolveXMLNamespace((BLangFieldBasedAccess.BLangPrefixedFieldBasedAccess) fieldAccessExpr, data); } @@ -8747,7 +8710,8 @@ private BType checkOptionalFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr BType laxFieldAccessType = getLaxFieldAccessType(((BLangFieldBasedAccess) fieldAccessExpr.expr).originalType); actualType = accessCouldResultInError(referredType) ? - BUnionType.create(null, laxFieldAccessType, symTable.errorType) : laxFieldAccessType; + BUnionType.create(typeEnv, null, laxFieldAccessType, symTable.errorType) : + laxFieldAccessType; if (fieldAccessExpr.fieldKind == FieldKind.WITH_NS) { resolveXMLNamespace((BLangFieldBasedAccess.BLangPrefixedFieldBasedAccess) fieldAccessExpr, data); } @@ -8761,31 +8725,16 @@ private BType checkOptionalFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr } if (nillableExprType && actualType != symTable.semanticError && !actualType.isNullable()) { - actualType = BUnionType.create(null, actualType, symTable.nilType); + actualType = BUnionType.create(typeEnv, null, actualType, symTable.nilType); } return actualType; } private boolean accessCouldResultInError(BType bType) { - BType type = Types.getImpliedType(bType); - if (type.tag == TypeTags.JSON) { - return true; - } - - if (type.tag == TypeTags.MAP) { - return false; - } - - if (types.isAssignable(bType, symTable.xmlType)) { - return true; - } - - if (type.tag == TypeTags.UNION) { - return ((BUnionType) type).getMemberTypes().stream().anyMatch(this::accessCouldResultInError); - } else { - return false; - } + SemType s = bType.semType(); + return SemTypes.containsBasicType(s, PredefinedType.XML) || + SemTypes.containsType(types.semTypeCtx, s, Core.createJson(types.semTypeCtx)); } private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, AnalyzerData data) { @@ -8808,9 +8757,9 @@ private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, A if (nillableExprType) { varRefType = nilRemovedSet.size() == 1 ? nilRemovedSet.iterator().next() : - BUnionType.create(null, nilRemovedSet); + BUnionType.create(typeEnv, null, nilRemovedSet); - if (!types.isSubTypeOfMapping(varRefType)) { + if (!types.isSubTypeOfMapping(varRefType.semType())) { // Member access is allowed on optional types only with mappings. dlog.error(indexBasedAccessExpr.pos, DiagnosticErrorCode.OPERATION_DOES_NOT_SUPPORT_MEMBER_ACCESS, @@ -8832,7 +8781,10 @@ private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, A BLangExpression indexExpr = indexBasedAccessExpr.indexExpr; BType actualType = symTable.semanticError; - if (types.isSubTypeOfMapping(varRefType)) { + if (varRefType == symTable.semanticError) { + indexBasedAccessExpr.indexExpr.setBType(symTable.semanticError); + return symTable.semanticError; + } else if (types.isSubTypeOfMapping(varRefType.semType())) { checkExpr(indexExpr, symTable.stringType, data); if (indexExpr.getBType() == symTable.semanticError) { @@ -8940,9 +8892,6 @@ private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, A actualType = types.addNilForNillableAccessType(constraint); indexBasedAccessExpr.originalType = indexBasedAccessExpr.leafNode || !nillableExprType ? actualType : types.getTypeWithoutNil(actualType); - } else if (varRefType == symTable.semanticError) { - indexBasedAccessExpr.indexExpr.setBType(symTable.semanticError); - return symTable.semanticError; } else { indexBasedAccessExpr.indexExpr.setBType(symTable.semanticError); dlog.error(indexBasedAccessExpr.pos, DiagnosticErrorCode.OPERATION_DOES_NOT_SUPPORT_MEMBER_ACCESS, @@ -8951,7 +8900,7 @@ private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, A } if (nillableExprType && !actualType.isNullable()) { - actualType = BUnionType.create(null, actualType, symTable.nilType); + actualType = BUnionType.create(typeEnv, null, actualType, symTable.nilType); } return actualType; @@ -8972,7 +8921,7 @@ private BType getXmlMemberAccessType(BType varRefType) { effectiveMemberTypes.add(getXMLConstituents(memberType)); } xmlMemberAccessType = effectiveMemberTypes.size() == 1 ? effectiveMemberTypes.iterator().next() : - BUnionType.create(null, effectiveMemberTypes); + BUnionType.create(typeEnv, null, effectiveMemberTypes); } else { xmlMemberAccessType = getXMLConstituents(varRefType); } @@ -8982,7 +8931,7 @@ private BType getXmlMemberAccessType(BType varRefType) { } else if (types.isAssignable(symTable.xmlNeverType, xmlMemberAccessType)) { return xmlMemberAccessType; } - return BUnionType.create(null, xmlMemberAccessType, symTable.xmlNeverType); + return BUnionType.create(typeEnv, null, xmlMemberAccessType, symTable.xmlNeverType); } private Long getConstIndex(BLangExpression indexExpr) { @@ -9024,22 +8973,23 @@ private BType checkArrayIndexBasedAccess(BLangIndexBasedAccess indexBasedAccess, return arrayType.eType; } Long indexVal = getConstIndex(indexExpr); - return indexVal >= arrayType.size || indexVal < 0 ? symTable.semanticError : arrayType.eType; + return indexVal >= arrayType.getSize() || indexVal < 0 ? symTable.semanticError : arrayType.eType; } switch (tag) { case TypeTags.FINITE: - BFiniteType finiteIndexExpr = (BFiniteType) indexExprType; - boolean validIndexExists = false; - for (BLangExpression finiteMember : finiteIndexExpr.getValueSpace()) { - int indexValue = ((Long) ((BLangLiteral) finiteMember).value).intValue(); - if (indexValue >= 0 && - (arrayType.state == BArrayState.OPEN || indexValue < arrayType.size)) { - validIndexExists = true; - break; - } + SemType t = indexExprType.semType(); + long maxIndexValue; + if (arrayType.state == BArrayState.OPEN) { + maxIndexValue = Long.MAX_VALUE; + } else { + maxIndexValue = arrayType.getSize() - 1; } - if (!validIndexExists) { + + SemType allowedInts = PredefinedType.basicSubtype(BasicTypeCode.BT_INT, + IntSubtype.createSingleRangeSubtype(0, maxIndexValue)); + + if (Core.isEmpty(types.semTypeCtx, SemTypes.intersect(t, allowedInts))) { return symTable.semanticError; } actualType = arrayType.eType; @@ -9048,7 +8998,7 @@ private BType checkArrayIndexBasedAccess(BLangIndexBasedAccess indexBasedAccess, // address the case where we have a union of types List finiteTypes = new ArrayList<>(); for (BType memType : ((BUnionType) indexExprType).getMemberTypes()) { - memType = Types.getImpliedType(memType); + memType = Types.getReferredType(memType); if (memType.tag == TypeTags.FINITE) { finiteTypes.add((BFiniteType) memType); } else { @@ -9059,7 +9009,12 @@ private BType checkArrayIndexBasedAccess(BLangIndexBasedAccess indexBasedAccess, } } if (!finiteTypes.isEmpty()) { - BFiniteType finiteType = createFiniteTypeFromFiniteTypeList(finiteTypes); + List newValueSpace = new ArrayList<>(); + for (BFiniteType ft : finiteTypes) { + newValueSpace.addAll(Arrays.asList(ft.valueSpace)); + } + + BFiniteType finiteType = new BFiniteType(null, newValueSpace.toArray(SemNamedType[]::new)); BType possibleType = checkArrayIndexBasedAccess(indexBasedAccess, finiteType, arrayType); if (possibleType == symTable.semanticError) { return symTable.semanticError; @@ -9099,7 +9054,7 @@ private BType checkListIndexBasedAccess(BLangIndexBasedAccess accessExpr, BType if (fieldTypeMembers.size() == 1) { return fieldTypeMembers.iterator().next(); } - return BUnionType.create(null, fieldTypeMembers); + return BUnionType.create(typeEnv, null, fieldTypeMembers); } private BType checkTupleIndexBasedAccess(BLangIndexBasedAccess accessExpr, BTupleType tuple, BType currentType) { @@ -9114,27 +9069,36 @@ private BType checkTupleIndexBasedAccess(BLangIndexBasedAccess accessExpr, BTupl } LinkedHashSet tupleTypes = collectTupleFieldTypes(tuple, new LinkedHashSet<>()); - return tupleTypes.size() == 1 ? tupleTypes.iterator().next() : BUnionType.create(null, tupleTypes); + return tupleTypes.size() == 1 ? tupleTypes.iterator().next() : + BUnionType.create(typeEnv, null, tupleTypes); } switch (tag) { case TypeTags.FINITE: - BFiniteType finiteIndexExpr = (BFiniteType) currentType; LinkedHashSet possibleTypes = new LinkedHashSet<>(); - for (BLangExpression finiteMember : finiteIndexExpr.getValueSpace()) { - int indexValue = ((Long) ((BLangLiteral) finiteMember).value).intValue(); - BType fieldType = checkTupleFieldType(tuple, indexValue); - if (fieldType.tag != TypeTags.SEMANTIC_ERROR) { - possibleTypes.add(fieldType); + SemType t = currentType.semType(); + + Optional properSubtypeData = getProperSubtypeData(t, BT_INT); + if (properSubtypeData.isEmpty()) { + return symTable.semanticError; + } + + IntSubtype intSubtype = (IntSubtype) properSubtypeData.get(); + for (Range range : intSubtype.ranges) { + for (long indexVal = range.min; indexVal <= range.max; indexVal++) { + BType fieldType = checkTupleFieldType(tuple, (int) indexVal); + if (fieldType.tag != TypeTags.SEMANTIC_ERROR) { + possibleTypes.add(fieldType); + } } } + if (possibleTypes.isEmpty()) { return symTable.semanticError; } actualType = possibleTypes.size() == 1 ? possibleTypes.iterator().next() : - BUnionType.create(null, possibleTypes); + BUnionType.create(typeEnv, null, possibleTypes); break; - case TypeTags.UNION: LinkedHashSet possibleTypesByMember = new LinkedHashSet<>(); List finiteTypes = new ArrayList<>(); @@ -9151,8 +9115,14 @@ private BType checkTupleIndexBasedAccess(BLangIndexBasedAccess accessExpr, BTupl } } }); + if (!finiteTypes.isEmpty()) { - BFiniteType finiteType = createFiniteTypeFromFiniteTypeList(finiteTypes); + List newValueSpace = new ArrayList<>(); + for (BFiniteType ft : finiteTypes) { + newValueSpace.addAll(Arrays.asList(ft.valueSpace)); + } + + BFiniteType finiteType = new BFiniteType(null, newValueSpace.toArray(SemNamedType[]::new)); BType possibleType = checkTupleIndexBasedAccess(accessExpr, tuple, finiteType); if (possibleType.tag == TypeTags.UNION) { possibleTypesByMember.addAll(((BUnionType) possibleType).getMemberTypes()); @@ -9160,11 +9130,12 @@ private BType checkTupleIndexBasedAccess(BLangIndexBasedAccess accessExpr, BTupl possibleTypesByMember.add(possibleType); } } + if (possibleTypesByMember.contains(symTable.semanticError)) { return symTable.semanticError; } actualType = possibleTypesByMember.size() == 1 ? possibleTypesByMember.iterator().next() : - BUnionType.create(null, possibleTypesByMember); + BUnionType.create(typeEnv, null, possibleTypesByMember); } return actualType; } @@ -9222,7 +9193,7 @@ private BType checkMappingIndexBasedAccess(BLangIndexBasedAccess accessExpr, BTy if (fieldTypeMembers.size() == 1) { fieldType = fieldTypeMembers.iterator().next(); } else { - fieldType = BUnionType.create(null, fieldTypeMembers); + fieldType = BUnionType.create(typeEnv, null, fieldTypeMembers); } return nonMatchedRecordExists ? types.addNilForNillableAccessType(fieldType) : fieldType; @@ -9274,13 +9245,19 @@ private BType checkRecordIndexBasedAccess(BLangIndexBasedAccess accessExpr, BRec } actualType = fieldTypes.size() == 1 ? fieldTypes.iterator().next() : - BUnionType.create(null, fieldTypes); + BUnionType.create(typeEnv, null, fieldTypes); break; case TypeTags.FINITE: - BFiniteType finiteIndexExpr = (BFiniteType) currentType; LinkedHashSet possibleTypes = new LinkedHashSet<>(); - for (BLangExpression finiteMember : finiteIndexExpr.getValueSpace()) { - String fieldName = (String) ((BLangLiteral) finiteMember).value; + SemType t = currentType.semType(); + + Optional properSubtypeData = getProperSubtypeData(t, BT_STRING); + if (properSubtypeData.isEmpty()) { + return symTable.semanticError; + } + + Set values = getStringValueSpace((StringSubtype) properSubtypeData.get()); + for (String fieldName : values) { BType fieldType = checkRecordRequiredFieldAccess(accessExpr, Names.fromString(fieldName), record, data); if (fieldType == symTable.semanticError) { @@ -9311,7 +9288,7 @@ private BType checkRecordIndexBasedAccess(BLangIndexBasedAccess accessExpr, BRec } actualType = possibleTypes.size() == 1 ? possibleTypes.iterator().next() : - BUnionType.create(null, possibleTypes); + BUnionType.create(typeEnv, null, possibleTypes); break; case TypeTags.UNION: LinkedHashSet possibleTypesByMember = new LinkedHashSet<>(); @@ -9328,8 +9305,14 @@ private BType checkRecordIndexBasedAccess(BLangIndexBasedAccess accessExpr, BRec } } }); + if (!finiteTypes.isEmpty()) { - BFiniteType finiteType = createFiniteTypeFromFiniteTypeList(finiteTypes); + List newValueSpace = new ArrayList<>(); + for (BFiniteType ft : finiteTypes) { + newValueSpace.addAll(Arrays.asList(ft.valueSpace)); + } + + BFiniteType finiteType = new BFiniteType(null, newValueSpace.toArray(SemNamedType[]::new)); BType possibleType = checkRecordIndexBasedAccess(accessExpr, record, finiteType, data); if (possibleType.tag == TypeTags.UNION) { possibleTypesByMember.addAll(((BUnionType) possibleType).getMemberTypes()); @@ -9337,15 +9320,53 @@ private BType checkRecordIndexBasedAccess(BLangIndexBasedAccess accessExpr, BRec possibleTypesByMember.add(possibleType); } } + if (possibleTypesByMember.contains(symTable.semanticError)) { return symTable.semanticError; } actualType = possibleTypesByMember.size() == 1 ? possibleTypesByMember.iterator().next() : - BUnionType.create(null, possibleTypesByMember); + BUnionType.create(typeEnv, null, possibleTypesByMember); } return actualType; } + private Optional getProperSubtypeData(SemType t, BasicTypeCode u) { + if (t instanceof BasicTypeBitSet) { + return Optional.empty(); + } + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, u); + if (sd instanceof AllOrNothingSubtype) { + return Optional.empty(); + } + return Optional.of(sd); + } + + /** + * Returns the set of values belongs to a given StringSubtype. + *

+ * Note: We assume StringSubtype does not contain any diff. i.e. contains only a finite set of values + * + * @param stringSubtype string subtype data + * @return set of string values + */ + private static Set getStringValueSpace(StringSubtype stringSubtype) { + Set values = new HashSet<>(); + CharStringSubtype charStringSubtype = stringSubtype.getChar(); + assert charStringSubtype.allowed; + for (EnumerableType enumerableType : charStringSubtype.values()) { + EnumerableCharString s = (EnumerableCharString) enumerableType; + values.add(s.value); + } + + NonCharStringSubtype nonCharStringSubtype = stringSubtype.getNonChar(); + assert nonCharStringSubtype.allowed; + for (EnumerableType enumerableType : nonCharStringSubtype.values()) { + EnumerableString s = (EnumerableString) enumerableType; + values.add(s.value); + } + return values; + } + private boolean isConstExpr(BLangExpression expression) { switch (expression.getKind()) { case LITERAL: @@ -9405,7 +9426,7 @@ private BType getRepresentativeBroadType(List inferredTypeList) { return inferredTypeList.get(0); } - return BUnionType.create(null, inferredTypeList.toArray(new BType[0])); + return BUnionType.create(typeEnv, null, inferredTypeList.toArray(new BType[0])); } public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType expType, AnalyzerData data) { @@ -9425,7 +9446,7 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex if (key.computedKey) { checkExpr(keyExpr, symTable.stringType, data); BType exprType = checkExpr(expression, expType, data); - if (isUniqueType(restFieldTypes, exprType)) { + if (types.isUniqueType(restFieldTypes, exprType)) { restFieldTypes.add(exprType); } } else { @@ -9442,7 +9463,7 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex if (type.tag == TypeTags.MAP) { BType constraintType = ((BMapType) type).constraint; - if (isUniqueType(restFieldTypes, constraintType)) { + if (types.isUniqueType(restFieldTypes, constraintType)) { restFieldTypes.add(constraintType); } } @@ -9459,7 +9480,7 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex if (!recordType.sealed) { BType restFieldType = recordType.restFieldType; - if (isUniqueType(restFieldTypes, restFieldType)) { + if (types.isUniqueType(restFieldTypes, restFieldType)) { restFieldTypes.add(restFieldType); } } @@ -9485,7 +9506,8 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex String key = entry.getKey(); Name fieldName = Names.fromString(key); - BType type = types.size() == 1 ? types.get(0) : BUnionType.create(null, types.toArray(new BType[0])); + BType type = types.size() == 1 ? types.get(0) : + BUnionType.create(typeEnv, null, types.toArray(new BType[0])); Set flags = new HashSet<>(); @@ -9507,7 +9529,7 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex recordSymbol.scope.define(fieldName, fieldSymbol); } - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(typeEnv, recordSymbol); recordType.fields = fields; if (restFieldTypes.contains(symTable.semanticError)) { @@ -9520,13 +9542,14 @@ public BType defineInferredRecordType(BLangRecordLiteral recordLiteral, BType ex } else if (restFieldTypes.size() == 1) { recordType.restFieldType = restFieldTypes.get(0); } else { - recordType.restFieldType = BUnionType.create(null, restFieldTypes.toArray(new BType[0])); + recordType.restFieldType = + BUnionType.create(typeEnv, null, restFieldTypes.toArray(new BType[0])); } recordSymbol.type = recordType; recordType.tsymbol = recordSymbol; if (expType == symTable.readonlyType || (recordType.sealed && allReadOnlyNonRestFields)) { - recordType.flags |= Flags.READONLY; + recordType.addFlags(Flags.READONLY); recordSymbol.flags |= Flags.READONLY; } @@ -9542,7 +9565,7 @@ private BRecordTypeSymbol createRecordTypeSymbol(PackageID pkgID, Location locat SymbolEnv env = data.env; BRecordTypeSymbol recordSymbol = Symbols.createRecordSymbol(Flags.ANONYMOUS, - Names.fromString(anonymousModelHelper.getNextAnonymousTypeKey(pkgID)), + Names.fromString(anonymousModelHelper.getNextAnonymousTypeKey(pkgID)), pkgID, null, env.scope.owner, location, origin); recordSymbol.scope = new Scope(recordSymbol); return recordSymbol; @@ -9564,7 +9587,7 @@ private void addToNonRestFieldTypes(Map nonRestFieldTypes, St FieldInfo fieldInfo = nonRestFieldTypes.get(keyString); List typeList = fieldInfo.types; - if (isUniqueType(typeList, exprType)) { + if (types.isUniqueType(typeList, exprType)) { typeList.add(exprType); } @@ -9573,23 +9596,6 @@ private void addToNonRestFieldTypes(Map nonRestFieldTypes, St } } - private boolean isUniqueType(Iterable typeList, BType type) { - type = Types.getImpliedType(type); - boolean isRecord = type.tag == TypeTags.RECORD; - - for (BType bType : typeList) { - bType = Types.getImpliedType(bType); - if (isRecord) { - if (type == bType) { - return false; - } - } else if (types.isSameType(type, bType)) { - return false; - } - } - return true; - } - private BType checkXmlSubTypeLiteralCompatibility(Location location, BXMLSubType mutableXmlSubType, BType expType, AnalyzerData data) { if (expType == symTable.semanticError) { @@ -9661,7 +9667,7 @@ private BType checkXmlSubTypeLiteralCompatibility(Location location, BXMLSubType private void markChildrenAsImmutable(BLangXMLElementLiteral bLangXMLElementLiteral, AnalyzerData data) { for (BLangExpression modifiedChild : bLangXMLElementLiteral.modifiedChildren) { BType childType = modifiedChild.getBType(); - if (Symbols.isFlagOn(childType.flags, Flags.READONLY) || + if (Symbols.isFlagOn(childType.getFlags(), Flags.READONLY) || !types.isSelectivelyImmutableType(childType, data.env.enclPkg.packageID)) { continue; } @@ -9681,7 +9687,7 @@ public void logUndefinedSymbolError(Location pos, String name) { } private void markTypeAsIsolated(BType actualType) { - actualType.flags |= Flags.ISOLATED; + actualType.addFlags(Flags.ISOLATED); actualType.tsymbol.flags |= Flags.ISOLATED; } @@ -9714,7 +9720,7 @@ private void handleObjectConstrExprForReadOnly( } classDefForConstructor.flagSet.add(Flag.READONLY); - actualObjectType.flags |= Flags.READONLY; + actualObjectType.addFlags(Flags.READONLY); actualObjectType.tsymbol.flags |= Flags.READONLY; ImmutableTypeCloner.markFieldsAsImmutable(classDefForConstructor, env, actualObjectType, types, @@ -9727,7 +9733,7 @@ private void markConstructedObjectIsolatedness(BObjectType actualObjectType) { if (actualObjectType.markedIsolatedness) { return; } - if (Symbols.isFlagOn(actualObjectType.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(actualObjectType.getFlags(), Flags.READONLY)) { markTypeAsIsolated(actualObjectType); return; } @@ -9786,7 +9792,7 @@ private BType validateElvisExprLhsExpr(BLangElvisExpr elvisExpr, BType lhsType) } else if (size == 1) { actualType = memberTypes.iterator().next(); } else { - actualType = BUnionType.create(null, memberTypes); + actualType = BUnionType.create(typeEnv, null, memberTypes); } } else { // We should get here only for `any` and nil. We use the type as is since we don't have a way to @@ -9805,25 +9811,22 @@ private BType validateElvisExprLhsExpr(BLangElvisExpr elvisExpr, BType lhsType) private LinkedHashSet getTypeWithoutNilForNonAnyTypeWithNil(BType type) { BType referredType = Types.getImpliedType(type); if (referredType.tag == TypeTags.FINITE) { - Set valueSpace = ((BFiniteType) referredType).getValueSpace(); - LinkedHashSet nonNilValueSpace = new LinkedHashSet<>(); - for (BLangExpression expression : valueSpace) { - if (expression.getBType().tag != TypeTags.NIL) { - nonNilValueSpace.add(expression); + BFiniteType finiteType = (BFiniteType) referredType; + List newValueSpace = new ArrayList<>(finiteType.valueSpace.length); + for (SemNamedType semNamedType : finiteType.valueSpace) { + if (!PredefinedType.NIL.equals(semNamedType.semType())) { + newValueSpace.add(semNamedType);; } } - int nonNilValueSpaceSize = nonNilValueSpace.size(); - - if (nonNilValueSpaceSize == valueSpace.size()) { - return new LinkedHashSet<>(1) {{ add(referredType); }}; - } - - if (nonNilValueSpaceSize == 0) { + if (newValueSpace.isEmpty()) { return new LinkedHashSet<>(0); } - return new LinkedHashSet<>(1) {{ add(new BFiniteType(null, nonNilValueSpace)); }}; + BFiniteType ft = new BFiniteType(null, newValueSpace.toArray(SemNamedType[]::new)); + return new LinkedHashSet<>(1) {{ + add(ft); + }}; } BUnionType unionType = (BUnionType) referredType; @@ -9844,16 +9847,6 @@ private LinkedHashSet getTypeWithoutNilForNonAnyTypeWithNil(BType type) { return memberTypes; } - private BFiniteType createFiniteTypeFromFiniteTypeList(List finiteTypes) { - if (finiteTypes.size() == 1) { - return finiteTypes.get(0); - } else { - Set valueSpace = new LinkedHashSet<>(); - finiteTypes.forEach(constituent -> valueSpace.addAll(constituent.getValueSpace())); - return new BFiniteType(null, valueSpace); - } - } - private static class FieldInfo { List types; boolean required; @@ -9867,6 +9860,7 @@ private FieldInfo(List types, boolean required, boolean readonly) { } private static class TypeSymbolPair { + private final BVarSymbol fieldSymbol; private final BType determinedType; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeHashVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeHashVisitor.java index 28769a48e816..5e54c8a473a3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeHashVisitor.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeHashVisitor.java @@ -24,7 +24,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; @@ -36,7 +35,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -53,7 +51,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLSubType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -73,7 +70,7 @@ * * @since 2.0.0 */ -public class TypeHashVisitor implements UniqueTypeVisitor { +public class TypeHashVisitor extends UniqueTypeVisitor { private final Map visited; private final Set unresolvedTypes; private final Map cache; @@ -106,7 +103,7 @@ public Integer getHash(BType type) { } @Override - public Integer visit(BType type) { + public Integer visit(BType type) { // TODO: can move to the abstract class? if (type == null) { return 0; } @@ -115,7 +112,7 @@ public Integer visit(BType type) { case TypeTags.ANY: return visit((BAnyType) type); case TypeTags.NIL: - return visit((BNilType) type); + return visitNilType(type); case TypeTags.NEVER: return visit((BNeverType) type); case TypeTags.ANYDATA: @@ -202,12 +199,12 @@ public Integer visit(BArrayType type) { if (isCyclic(type)) { return 0; } - Integer hash = hash(baseHash(type), type.size, type.state.getValue(), visit(type.eType)); + Integer hash = hash(baseHash(type), type.getSize(), type.state.getValue(), visit(type.eType)); return addToVisited(type, hash); } @Override - public Integer visit(BBuiltInRefType type) { + public Integer visit(BReadonlyType type) { if (isVisited(type)) { return visited.get(type); } @@ -369,7 +366,7 @@ public Integer visit(BNeverType type) { } @Override - public Integer visit(BNilType type) { + public Integer visitNilType(BType type) { if (isVisited(type)) { return visited.get(type); } @@ -406,7 +403,7 @@ public Integer visit(BTupleType type) { return 0; } List tupleTypesHashes = getOrderedTypesHashes(type.getTupleTypes()); - Integer hash = hash(baseHash(type), tupleTypesHashes, visit(type.restType), type.flags, type.tsymbol); + Integer hash = hash(baseHash(type), tupleTypesHashes, visit(type.restType), type.getFlags(), type.tsymbol); return addToVisited(type, hash); } @@ -455,18 +452,8 @@ public Integer visit(BFiniteType type) { if (isCyclic(type)) { return 0; } - List toSort = new ArrayList<>(); - for (BLangExpression bLangExpression : type.getValueSpace()) { - String toString = bLangExpression.toString(); - toSort.add(toString); - } - toSort.sort(null); - List valueSpaceHashes = new ArrayList<>(); - for (String toString : toSort) { - Integer hashCode = toString.hashCode(); - valueSpaceHashes.add(hashCode); - } - Integer hash = hash(baseHash(type), valueSpaceHashes); + + Integer hash = hash(baseHash(type), type.toString().hashCode()); return addToVisited(type, hash); } @@ -480,7 +467,7 @@ public Integer visit(BStructureType type) { } List fieldsHashes = getFieldsHashes(type.fields); List typeInclHashes = getTypesHashes(type.typeInclusions); - Integer hash = hash(baseHash(type), type.flags, fieldsHashes, typeInclHashes); + Integer hash = hash(baseHash(type), type.getFlags(), fieldsHashes, typeInclHashes); return addToVisited(type, hash); } @@ -495,7 +482,7 @@ public Integer visit(BObjectType type) { List fieldsHashes = getFieldsHashes(type.fields); List typeInclHashes = getTypesHashes(type.typeInclusions); List attachedFunctionsHashes = getFunctionsHashes(((BObjectTypeSymbol) type.tsymbol).attachedFuncs); - Integer hash = hash(baseHash(type), type.flags, fieldsHashes, typeInclHashes, + Integer hash = hash(baseHash(type), type.getFlags(), fieldsHashes, typeInclHashes, attachedFunctionsHashes, type.typeIdSet); return addToVisited(type, hash); } @@ -510,7 +497,7 @@ public Integer visit(BRecordType type) { } List fieldsHashes = getFieldsHashes(type.fields); List typeInclHashes = getTypesHashes(type.typeInclusions); - Integer hash = hash(baseHash(type), type.flags, type.sealed, fieldsHashes, typeInclHashes, + Integer hash = hash(baseHash(type), type.getFlags(), type.sealed, fieldsHashes, typeInclHashes, visit(type.restFieldType)); return addToVisited(type, hash); } @@ -523,7 +510,7 @@ public Integer visit(BUnionType type) { if (isCyclic(type)) { return 0; } - Integer hash = hash(baseHash(type), type.isCyclic, getTypesHashes(type.getMemberTypes()), type.flags); + Integer hash = hash(baseHash(type), type.isCyclic, getTypesHashes(type.getMemberTypes()), type.getFlags()); return addToVisited(type, hash); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java index d2ee81e5d206..35ec2e2e95c7 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeNarrower.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.analyzer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.SemType; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.symbols.SymbolKind; import org.ballerinalang.model.tree.NodeKind; @@ -39,6 +40,7 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangBinaryExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; import org.wso2.ballerinalang.compiler.tree.expressions.BLangGroupExpr; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef; import org.wso2.ballerinalang.compiler.tree.expressions.BLangTypeTestExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangUnaryExpr; @@ -391,7 +393,7 @@ private BType getTypeUnion(BType currentType, BType targetType) { } else if (union.size() == 1) { return union.toArray(new BType[1])[0]; } - return BUnionType.create(null, union); + return BUnionType.create(symTable.typeEnv(), null, union); } BVarSymbol getOriginalVarSymbol(BVarSymbol varSymbol) { @@ -419,15 +421,17 @@ private BFiniteType createFiniteType(BLangExpression expr) { Flags.asMask(EnumSet.noneOf(Flag.class)), Names.EMPTY, env.enclPkg.symbol.pkgID, null, env.scope.owner, expr.pos, SOURCE); - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); + SemType semType; if (expr.getKind() == NodeKind.UNARY_EXPR) { - finiteType.addValue(Types.constructNumericLiteralFromUnaryExpr((BLangUnaryExpr) expr)); + semType = SemTypeHelper.resolveSingletonType(Types.constructNumericLiteralFromUnaryExpr( + (BLangUnaryExpr) expr)); } else { expr.setBType(symTable.getTypeFromTag(expr.getBType().tag)); - finiteType.addValue(expr); + semType = SemTypeHelper.resolveSingletonType((BLangLiteral) expr); } - finiteTypeSymbol.type = finiteType; + BFiniteType finiteType = BFiniteType.newSingletonBFiniteType(finiteTypeSymbol, semType); + finiteTypeSymbol.type = finiteType; return finiteType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeParamAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeParamAnalyzer.java index 731978cb27fc..7b3eda99d419 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeParamAnalyzer.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeParamAnalyzer.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.analyzer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.Name; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.tree.NodeKind; @@ -130,7 +131,7 @@ private TypeParamAnalyzer(CompilerContext context) { static boolean isTypeParam(BType expType) { - return Symbols.isFlagOn(expType.flags, Flags.TYPE_PARAM) + return Symbols.isFlagOn(expType.getFlags(), Flags.TYPE_PARAM) || (expType.tsymbol != null && Symbols.isFlagOn(expType.tsymbol.flags, Flags.TYPE_PARAM)); } @@ -173,7 +174,7 @@ public BType getNominalType(BType type, Name name, long flag) { BType createTypeParam(BSymbol symbol) { BType type = symbol.type; - var flag = type.flags | Flags.TYPE_PARAM; + var flag = type.getFlags() | Flags.TYPE_PARAM; return createTypeParamType(symbol, type, symbol.name, flag); } @@ -222,7 +223,7 @@ private static boolean containsTypeParam(BType type, HashSet resolvedType return false; case TypeTags.INVOKABLE: BInvokableType invokableType = (BInvokableType) type; - if (Symbols.isFlagOn(invokableType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(invokableType.getFlags(), Flags.ANY_FUNCTION)) { return false; } for (BType paramType : invokableType.paramTypes) { @@ -277,9 +278,9 @@ private BType createBuiltInType(BType type, Name name, long flags) { TypeTags.DECIMAL, TypeTags.STRING, TypeTags.BOOLEAN -> new BType(tag, null, name, flags); - case TypeTags.ANY -> new BAnyType(tag, null, name, flags); + case TypeTags.ANY -> new BAnyType(name, flags); case TypeTags.ANYDATA -> createAnydataType((BUnionType) referredType, name, flags); - case TypeTags.READONLY -> new BReadonlyType(tag, null, name, flags); + case TypeTags.READONLY -> new BReadonlyType(flags); // For others, we will use TSymbol. default -> type; }; @@ -297,16 +298,17 @@ private BType createTypeParamType(BSymbol symbol, BType type, Name name, long fl case TypeTags.BOOLEAN: return new BType(type.tag, null, name, flags); case TypeTags.ANY: - return new BAnyType(type.tag, null, name, flags); + return new BAnyType(name, flags); case TypeTags.ANYDATA: return createAnydataType((BUnionType) type, name, flags); case TypeTags.READONLY: - return new BReadonlyType(type.tag, null, name, flags); + return new BReadonlyType(flags); case TypeTags.UNION: if (types.isCloneableType((BUnionType) refType)) { - BUnionType cloneableType = BUnionType.create(null, symTable.readonlyType, symTable.xmlType); + BUnionType cloneableType = + BUnionType.create(symTable.typeEnv(), null, symTable.readonlyType, symTable.xmlType); addCyclicArrayMapTableOfMapMembers(cloneableType); - cloneableType.flags = flags; + cloneableType.setFlags(flags); cloneableType.tsymbol = new BTypeSymbol(SymTag.TYPE, refType.tsymbol.flags, symbol.name, refType.tsymbol.pkgID, cloneableType, refType.tsymbol.owner, type.tsymbol.pos, @@ -323,20 +325,20 @@ private BType createTypeParamType(BSymbol symbol, BType type, Name name, long fl } private BAnydataType createAnydataType(BUnionType unionType, Name name, long flags) { - BAnydataType anydataType = new BAnydataType(unionType); + BAnydataType anydataType = new BAnydataType(types.typeCtx(), unionType); Optional immutableType = Types.getImmutableType(symTable, PackageID.ANNOTATIONS, unionType); immutableType.ifPresent(bIntersectionType -> Types.addImmutableType(symTable, PackageID.ANNOTATIONS, anydataType, bIntersectionType)); anydataType.name = name; - anydataType.flags |= flags; + anydataType.addFlags(flags); return anydataType; } private void addCyclicArrayMapTableOfMapMembers(BUnionType unionType) { - BArrayType arrayCloneableType = new BArrayType(unionType); - BMapType mapCloneableType = new BMapType(TypeTags.MAP, unionType, null); - BType tableMapCloneableType = new BTableType(TypeTags.TABLE, mapCloneableType, null); + BArrayType arrayCloneableType = new BArrayType(symTable.typeEnv(), unionType); + BMapType mapCloneableType = new BMapType(symTable.typeEnv(), TypeTags.MAP, unionType, null); + BType tableMapCloneableType = new BTableType(symTable.typeEnv(), mapCloneableType, null); unionType.add(arrayCloneableType); unionType.add(mapCloneableType); unionType.add(tableMapCloneableType); @@ -590,10 +592,10 @@ private void findTypeParamInStreamForUnion(Location loc, BStreamType expType, BU } } - BUnionType cUnionType = BUnionType.create(null, constraintTypes); + BUnionType cUnionType = BUnionType.create(symTable.typeEnv(), null, constraintTypes); findTypeParam(loc, expType.constraint, cUnionType, env, resolvedTypes, result); if (!completionTypes.isEmpty()) { - BUnionType eUnionType = BUnionType.create(null, completionTypes); + BUnionType eUnionType = BUnionType.create(symTable.typeEnv(), null, completionTypes); findTypeParam(loc, expType.completionType, eUnionType, env, resolvedTypes, result); } else { findTypeParam(loc, expType.completionType, symTable.nilType, env, resolvedTypes, result); @@ -615,7 +617,7 @@ private void findTypeParamInTable(Location loc, BTableType expType, BTableType a if (members.size() == 1) { findTypeParam(loc, expType.keyTypeConstraint, members.get(0).type, env, resolvedTypes, result); } else { - BTupleType tupleType = new BTupleType(members); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), members); findTypeParam(loc, expType.keyTypeConstraint, tupleType, env, resolvedTypes, result); } } @@ -632,7 +634,7 @@ private void findTypeParamInTupleForArray(Location loc, BArrayType expType, BTup int size = tupleTypes.size(); BType type = size == 0 ? symTable.neverType : - (size == 1 ? tupleTypes.iterator().next() : BUnionType.create(null, tupleTypes)); + (size == 1 ? tupleTypes.iterator().next() : BUnionType.create(symTable.typeEnv(), null, tupleTypes)); findTypeParam(loc, expType.eType, type, env, resolvedTypes, result); } @@ -663,7 +665,7 @@ private void findTypeParamInUnion(Location loc, BType expType, BUnionType actual ((BTupleType) referredType).getTupleTypes().forEach(member -> members.add(member)); } } - BUnionType tupleElementType = BUnionType.create(null, members); + BUnionType tupleElementType = BUnionType.create(symTable.typeEnv(), null, members); findTypeParam(loc, expType, tupleElementType, env, resolvedTypes, result); } @@ -703,7 +705,7 @@ private void findTypeParamInMapForRecord(Location loc, BMapType expType, BRecord if (reducedTypeSet.size() == 1) { commonFieldType = reducedTypeSet.iterator().next(); } else { - commonFieldType = BUnionType.create(null, reducedTypeSet); + commonFieldType = BUnionType.create(symTable.typeEnv(), null, reducedTypeSet); } findTypeParam(loc, expType.constraint, commonFieldType, env, resolvedTypes, result); @@ -712,7 +714,7 @@ private void findTypeParamInMapForRecord(Location loc, BMapType expType, BRecord private void findTypeParamInInvokableType(Location loc, BInvokableType expType, BInvokableType actualType, SymbolEnv env, HashSet resolvedTypes, FindTypeParamResult result) { - if (Symbols.isFlagOn(expType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(expType.getFlags(), Flags.ANY_FUNCTION)) { return; } for (int i = 0; i < expType.paramTypes.size() && i < actualType.paramTypes.size(); i++) { @@ -768,14 +770,14 @@ private void findTypeParamInError(Location loc, BErrorType expType, BType actual findTypeParam(loc, expType.detailType, ((BErrorType) actualType).detailType, env, resolvedTypes, result); } - if (actualType.tag == TypeTags.UNION && types.isSubTypeOfBaseType(actualType, TypeTags.ERROR)) { + if (actualType.tag == TypeTags.UNION && types.isSubTypeOfBaseType(actualType, PredefinedType.ERROR)) { BUnionType errorUnion = (BUnionType) actualType; LinkedHashSet errorDetailTypes = new LinkedHashSet<>(); for (BType errorType : errorUnion.getMemberTypes()) { BType member = Types.getImpliedType(errorType); errorDetailTypes.add(((BErrorType) member).detailType); } - BUnionType errorDetailUnionType = BUnionType.create(null, errorDetailTypes); + BUnionType errorDetailUnionType = BUnionType.create(symTable.typeEnv(), null, errorDetailTypes); findTypeParam(loc, expType.detailType, errorDetailUnionType, env, resolvedTypes, result); } } @@ -802,14 +804,15 @@ private BType getMatchingBoundType(BType expType, SymbolEnv env, HashSet if (!isDifferentTypes(elementType, matchingBoundElementType)) { return expType; } - return new BArrayType(matchingBoundElementType); + return new BArrayType(symTable.typeEnv(), matchingBoundElementType); case TypeTags.MAP: BType constraint = ((BMapType) expType).constraint; BType matchingBoundMapConstraintType = getMatchingBoundType(constraint, env, resolvedTypes); if (!isDifferentTypes(constraint, matchingBoundMapConstraintType)) { return expType; } - return new BMapType(TypeTags.MAP, matchingBoundMapConstraintType, symTable.mapType.tsymbol); + return new BMapType(symTable.typeEnv(), TypeTags.MAP, matchingBoundMapConstraintType, + symTable.mapType.tsymbol); case TypeTags.STREAM: BStreamType expStreamType = (BStreamType) expType; BType expStreamConstraint = expStreamType.constraint; @@ -826,7 +829,8 @@ private BType getMatchingBoundType(BType expType, SymbolEnv env, HashSet return expStreamType; } - return new BStreamType(TypeTags.STREAM, constraintType, completionType, symTable.streamType.tsymbol); + return new BStreamType(symTable.typeEnv(), TypeTags.STREAM, constraintType, completionType, + symTable.streamType.tsymbol); case TypeTags.TABLE: BTableType expTableType = (BTableType) expType; BType expTableConstraint = expTableType.constraint; @@ -844,7 +848,8 @@ private BType getMatchingBoundType(BType expType, SymbolEnv env, HashSet return expTableType; } - BTableType tableType = new BTableType(TypeTags.TABLE, tableConstraint, symTable.tableType.tsymbol); + BTableType tableType = new BTableType(symTable.typeEnv(), tableConstraint, + symTable.tableType.tsymbol); if (expTableKeyTypeConstraint != null) { tableType.keyTypeConstraint = keyTypeConstraint; } @@ -868,7 +873,7 @@ private BType getMatchingBoundType(BType expType, SymbolEnv env, HashSet return expType; } - return new BTypedescType(matchingBoundType, symTable.typeDesc.tsymbol); + return new BTypedescType(symTable.typeEnv(), matchingBoundType, symTable.typeDesc.tsymbol); case TypeTags.INTERSECTION: return getMatchingReadonlyIntersectionBoundType((BIntersectionType) expType, env, resolvedTypes); case TypeTags.TYPEREFDESC: @@ -910,7 +915,7 @@ private BType getMatchingReadonlyIntersectionBoundType(BIntersectionType interse } if (types.isInherentlyImmutableType(matchingBoundNonReadOnlyType) || - Symbols.isFlagOn(matchingBoundNonReadOnlyType.flags, Flags.READONLY)) { + Symbols.isFlagOn(matchingBoundNonReadOnlyType.getFlags(), Flags.READONLY)) { return matchingBoundNonReadOnlyType; } @@ -930,7 +935,7 @@ private BTupleType getMatchingTupleBoundType(BTupleType expType, SymbolEnv env, if (!hasDifferentType && isDifferentTypes(type, matchingBoundType)) { hasDifferentType = true; } - BVarSymbol varSymbol = new BVarSymbol(matchingBoundType.flags, null, null, matchingBoundType, + BVarSymbol varSymbol = new BVarSymbol(matchingBoundType.getFlags(), null, null, matchingBoundType, null, null, null); members.add(new BTupleMember(matchingBoundType, varSymbol)); } @@ -947,7 +952,7 @@ private BTupleType getMatchingTupleBoundType(BTupleType expType, SymbolEnv env, return expType; } - return new BTupleType(members); + return new BTupleType(symTable.typeEnv(), members); } private BRecordType getMatchingRecordBoundType(BRecordType expType, SymbolEnv env, HashSet resolvedTypes) { @@ -976,10 +981,10 @@ private BRecordType getMatchingRecordBoundType(BRecordType expType, SymbolEnv en recordSymbol.scope.define(expField.name, field.symbol); } - BRecordType bRecordType = new BRecordType(recordSymbol); + BRecordType bRecordType = new BRecordType(symTable.typeEnv(), recordSymbol); bRecordType.fields = fields; recordSymbol.type = bRecordType; - bRecordType.flags = expType.flags; + bRecordType.setFlags(expType.getFlags()); if (expType.sealed) { bRecordType.sealed = true; @@ -1010,7 +1015,7 @@ private BInvokableType getMatchingFunctionBoundType(BInvokableType expType, Symb } BType restType = expType.restType; - var flags = expType.flags; + var flags = expType.getFlags(); BInvokableTypeSymbol invokableTypeSymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, flags, env.enclPkg.symbol.pkgID, expType, env.scope.owner, expType.tsymbol.pos, VIRTUAL); @@ -1027,7 +1032,7 @@ private BInvokableType getMatchingFunctionBoundType(BInvokableType expType, Symb return expType; } - BInvokableType invokableType = new BInvokableType(paramTypes, restType, + BInvokableType invokableType = new BInvokableType(symTable.typeEnv(), paramTypes, restType, matchingBoundType, invokableTypeSymbol); invokableTypeSymbol.returnType = invokableType.retType; @@ -1035,7 +1040,7 @@ private BInvokableType getMatchingFunctionBoundType(BInvokableType expType, Symb invokableType.tsymbol.isTypeParamResolved = true; invokableType.tsymbol.typeParamTSymbol = expType.tsymbol; if (Symbols.isFlagOn(flags, Flags.ISOLATED)) { - invokableType.flags |= Flags.ISOLATED; + invokableType.addFlags(Flags.ISOLATED); } return invokableType; @@ -1052,7 +1057,7 @@ private BType getMatchingObjectBoundType(BObjectType expType, SymbolEnv env, Has actObjectSymbol.isTypeParamResolved = true; actObjectSymbol.typeParamTSymbol = expType.tsymbol; - BObjectType objectType = new BObjectType(actObjectSymbol); + BObjectType objectType = new BObjectType(symTable.typeEnv(), actObjectSymbol); actObjectSymbol.type = objectType; actObjectSymbol.scope = new Scope(actObjectSymbol); @@ -1134,7 +1139,7 @@ private BType getMatchingOptionalBoundType(BUnionType expType, SymbolEnv env, Ha return expType; } - return BUnionType.create(null, members); + return BUnionType.create(symTable.typeEnv(), null, members); } private BType getMatchingErrorBoundType(BErrorType expType, SymbolEnv env, HashSet resolvedTypes) { @@ -1156,7 +1161,7 @@ private BType getMatchingErrorBoundType(BErrorType expType, SymbolEnv env, HashS null, null, symTable.builtinPos, VIRTUAL); typeSymbol.isTypeParamResolved = true; typeSymbol.typeParamTSymbol = expType.tsymbol; - BErrorType errorType = new BErrorType(typeSymbol, detailType); + BErrorType errorType = new BErrorType(symTable.typeEnv(), typeSymbol, detailType); typeSymbol.type = errorType; return errorType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java index 189a1504468d..9812114fd3fb 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/TypeResolver.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.analyzer; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.SemType; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.symbols.SymbolKind; @@ -68,6 +69,7 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.tree.BLangClassDefinition; import org.wso2.ballerinalang.compiler.tree.BLangConstantValue; import org.wso2.ballerinalang.compiler.tree.BLangFunction; @@ -129,6 +131,7 @@ import static org.ballerinalang.model.symbols.SymbolOrigin.BUILTIN; import static org.ballerinalang.model.symbols.SymbolOrigin.SOURCE; import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL; +import static org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper.singleShapeBroadType; import static org.wso2.ballerinalang.compiler.util.Constants.INFERRED_ARRAY_INDICATOR; import static org.wso2.ballerinalang.compiler.util.Constants.OPEN_ARRAY_INDICATOR; @@ -377,7 +380,7 @@ public void defineClassDef(BLangClassDefinition classDefinition, SymbolEnv env) typeFlags |= Flags.OBJECT_CTOR; } - BObjectType objectType = new BObjectType(tSymbol, typeFlags); + BObjectType objectType = new BObjectType(symTable.typeEnv(), tSymbol, typeFlags); resolvingStructureTypes.add(objectType); if (classDefinition.isObjectContructorDecl || flags.contains(Flag.OBJECT_CTOR)) { classDefinition.oceEnvData.objectType = objectType; @@ -389,7 +392,7 @@ public void defineClassDef(BLangClassDefinition classDefinition, SymbolEnv env) } if (flags.contains(Flag.CLIENT)) { - objectType.flags |= Flags.CLIENT; + objectType.addFlags(Flags.CLIENT); } tSymbol.type = objectType; @@ -492,7 +495,7 @@ private void handleDistinctDefinitionOfErrorIntersection(BLangTypeDefinition typ } if (!effectiveType.typeIdSet.isEmpty()) { - definedType.flags |= Flags.DISTINCT; + definedType.addFlags(Flags.DISTINCT); } } } @@ -574,7 +577,7 @@ private void updateIsCyclicFlag(BType type) { ((BUnionType) type).isCyclic = true; break; case INTERSECTION: - updateIsCyclicFlag(((BIntersectionType) type).getEffectiveType()); + updateIsCyclicFlag(((BIntersectionType) type).effectiveType); break; } } @@ -646,8 +649,8 @@ public BType validateModuleLevelDef(String name, Name pkgAlias, Name typeName, B td.symbol = symbol; if (symbol.kind == SymbolKind.TYPE_DEF && !Symbols.isFlagOn(symbol.flags, Flags.ANONYMOUS)) { BType referenceType = ((BTypeDefinitionSymbol) symbol).referenceType; - referenceType.flags |= symbol.type.flags; - referenceType.tsymbol.flags |= symbol.type.flags; + referenceType.addFlags(symbol.type.getFlags()); + referenceType.tsymbol.flags |= symbol.type.getFlags(); return referenceType; } return resolvedType; @@ -761,7 +764,7 @@ private BType resolveTypedescTypeDesc(BLangConstrainedType td, ResolverData data SymbolEnv symEnv = data.env; BType type = resolveTypeDesc(symEnv, data.typeDefinition, data.depth + 1, td.type, data); - BTypedescType constrainedType = new BTypedescType(symTable.empty, null); + BTypedescType constrainedType = new BTypedescType(symTable.typeEnv(), symTable.empty, null); BTypeSymbol typeSymbol = type.tsymbol; constrainedType.tsymbol = Symbols.createTypeSymbol(typeSymbol.tag, typeSymbol.flags, typeSymbol.name, typeSymbol.originalName, symEnv.enclPkg.symbol.pkgID, constrainedType, typeSymbol.owner, @@ -780,7 +783,7 @@ private BType resolveFutureTypeDesc(BLangConstrainedType td, ResolverData data) SymbolEnv symEnv = data.env; BType type = resolveTypeDesc(symEnv, data.typeDefinition, data.depth + 1, td.type, data); - BFutureType constrainedType = new BFutureType(TypeTags.FUTURE, symTable.empty, null); + BFutureType constrainedType = new BFutureType(symTable.typeEnv(), symTable.empty, null); BTypeSymbol typeSymbol = type.tsymbol; constrainedType.tsymbol = Symbols.createTypeSymbol(typeSymbol.tag, typeSymbol.flags, typeSymbol.name, typeSymbol.originalName, symEnv.enclPkg.symbol.pkgID, constrainedType, typeSymbol.owner, @@ -835,7 +838,7 @@ private BType resolveMapTypeDesc(BLangConstrainedType td, ResolverData data) { BTypeSymbol tSymbol = Symbols.createTypeSymbol(SymTag.TYPE, typeSymbol.flags, Names.EMPTY, typeSymbol.originalName, symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); - BMapType constrainedType = new BMapType(TypeTags.MAP, symTable.empty, tSymbol); + BMapType constrainedType = new BMapType(symTable.typeEnv(), TypeTags.MAP, symTable.empty, tSymbol); td.setBType(constrainedType); tSymbol.type = type; resolvingTypes.push(constrainedType); @@ -864,7 +867,7 @@ private BType resolveTypeDesc(BLangArrayType td, ResolverData data) { symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); BArrayType arrType; if (td.sizes.isEmpty()) { - arrType = new BArrayType(resultType, arrayTypeSymbol); + arrType = new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol); } else { BLangExpression size = td.sizes.get(i); if (size.getKind() == NodeKind.LITERAL || size.getKind() == NodeKind.NUMERIC_LITERAL) { @@ -877,7 +880,8 @@ private BType resolveTypeDesc(BLangArrayType td, ResolverData data) { } else { arrayState = BArrayState.CLOSED; } - arrType = new BArrayType(resultType, arrayTypeSymbol, sizeIndicator, arrayState); + arrType = + new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol, sizeIndicator, arrayState); } else { if (size.getKind() != NodeKind.SIMPLE_VARIABLE_REF) { dlog.error(size.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, symTable.intType, @@ -928,7 +932,8 @@ private BType resolveTypeDesc(BLangArrayType td, ResolverData data) { } else { length = (int) lengthCheck; } - arrType = new BArrayType(resultType, arrayTypeSymbol, length, BArrayState.CLOSED); + arrType = + new BArrayType(symTable.typeEnv(), resultType, arrayTypeSymbol, length, BArrayState.CLOSED); } } arrayTypeSymbol.type = arrType; @@ -965,7 +970,7 @@ private BType resolveTypeDesc(BLangTupleTypeNode td, ResolverData data) { Names.EMPTY, symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); List memberTypes = new ArrayList<>(); - BTupleType tupleType = new BTupleType(tupleTypeSymbol, memberTypes); + BTupleType tupleType = new BTupleType(symTable.typeEnv(), tupleTypeSymbol, memberTypes); tupleTypeSymbol.type = tupleType; td.setBType(tupleType); resolvingTypes.push(tupleType); @@ -974,7 +979,7 @@ private BType resolveTypeDesc(BLangTupleTypeNode td, ResolverData data) { BType type = resolveTypeDesc(symEnv, data.typeDefinition, data.depth + 1, memberNode.typeNode, data); SymbolEnv tupleEnv = SymbolEnv.createTypeEnv(td, new Scope(tupleTypeSymbol), symEnv); symEnter.defineNode(memberNode, tupleEnv); - BVarSymbol varSymbol = new BVarSymbol(memberNode.getBType().flags, memberNode.symbol.name, + BVarSymbol varSymbol = new BVarSymbol(memberNode.getBType().getFlags(), memberNode.symbol.name, memberNode.symbol.pkgID, memberNode.getBType(), memberNode.symbol.owner, memberNode.pos, SOURCE); memberTypes.add(new BTupleMember(type, varSymbol)); } @@ -1009,7 +1014,7 @@ private BType resolveTypeDesc(BLangRecordTypeNode td, ResolverData data) { symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, td.isAnonymous ? VIRTUAL : BUILTIN); - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordSymbol); resolvingStructureTypes.add(recordType); recordSymbol.type = recordType; td.symbol = recordSymbol; @@ -1076,7 +1081,7 @@ private BType resolveTypeDesc(BLangObjectTypeNode td, ResolverData data) { BTypeSymbol objectSymbol = Symbols.createObjectSymbol(Flags.asMask(flags), Names.EMPTY, symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); - BObjectType objectType = new BObjectType(objectSymbol, typeFlags); + BObjectType objectType = new BObjectType(symTable.typeEnv(), objectSymbol, typeFlags); resolvingStructureTypes.add(objectType); objectSymbol.type = objectType; td.symbol = objectSymbol; @@ -1113,7 +1118,7 @@ private BType resolveTypeDesc(BLangFunctionTypeNode td, ResolverData data) { SymbolEnv symEnv = data.env; Location pos = td.pos; - BInvokableType bInvokableType = new BInvokableType(null, null, null, null); + BInvokableType bInvokableType = new BInvokableType(symTable.typeEnv(), List.of(), null, null, null); BInvokableTypeSymbol tsymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, Flags.asMask(td.flagSet), symEnv.enclPkg.symbol.pkgID, bInvokableType, symEnv.scope.owner, pos, BUILTIN); @@ -1146,7 +1151,7 @@ public BType createInvokableType(List paramVars, List paramNames = new ArrayList<>(); BInvokableTypeSymbol tsymbol = (BInvokableTypeSymbol) bInvokableType.tsymbol; if (Symbols.isFlagOn(flags, Flags.ANY_FUNCTION)) { - bInvokableType.flags = flags; + bInvokableType.setFlags(flags); tsymbol.params = null; tsymbol.restParam = null; tsymbol.returnType = null; @@ -1202,7 +1207,7 @@ public BType createInvokableType(List paramVars, bInvokableType.paramTypes = paramTypes; bInvokableType.restType = restType; bInvokableType.retType = retType; - bInvokableType.flags |= flags; + bInvokableType.addFlags(flags); tsymbol.params = params; tsymbol.restParam = restParam; tsymbol.returnType = retType; @@ -1222,7 +1227,7 @@ private BType resolveTypeDesc(BLangErrorType td, ResolverData data) { } if (td.detailType == null) { - BType errorType = new BErrorType(null, symTable.detailType); + BType errorType = new BErrorType(symTable.typeEnv(), null, symTable.detailType); errorType.tsymbol = new BErrorTypeSymbol(SymTag.ERROR, Flags.PUBLIC, Names.ERROR, symTable.rootPkgSymbol.pkgID, errorType, symTable.rootPkgSymbol, symTable.builtinPos, BUILTIN); return errorType; @@ -1231,7 +1236,7 @@ private BType resolveTypeDesc(BLangErrorType td, ResolverData data) { // Define user define error type. BErrorTypeSymbol errorTypeSymbol = Symbols.createErrorSymbol(Flags.asMask(td.flagSet), Names.EMPTY, data.env.enclPkg.packageID, null, data.env.scope.owner, td.pos, BUILTIN); - BErrorType errorType = new BErrorType(errorTypeSymbol, symTable.empty); + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorTypeSymbol, symTable.empty); td.setBType(errorType); resolvingTypes.push(errorType); @@ -1262,7 +1267,7 @@ private BType resolveTypeDesc(BLangErrorType td, ResolverData data) { symEnter.defineSymbol(td.pos, errorTypeSymbol, data.env); } - errorType.flags |= errorTypeSymbol.flags; + errorType.addFlags(errorTypeSymbol.flags); errorTypeSymbol.type = errorType; symResolver.markParameterizedType(errorType, detailType); @@ -1286,7 +1291,7 @@ private BType resolveTypeDesc(BLangUnionTypeNode td, ResolverData data) { BTypeSymbol unionTypeSymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, Flags.asMask(EnumSet.of(Flag.PUBLIC)), Names.EMPTY, symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); - BUnionType unionType = new BUnionType(unionTypeSymbol, memberTypes, false, false); + BUnionType unionType = new BUnionType(types.typeEnv(), unionTypeSymbol, memberTypes, false); unionTypeSymbol.type = unionType; td.setBType(unionType); resolvingTypes.push(unionType); @@ -1298,63 +1303,37 @@ private BType resolveTypeDesc(BLangUnionTypeNode td, ResolverData data) { continue; } - if (resolvedType.isNullable()) { - unionType.setNullable(true); - } memberTypes.add(resolvedType); } - updateReadOnlyAndNullableFlag(unionType); + updateReadOnlyFlag(unionType); symResolver.markParameterizedType(unionType, memberTypes); resolvingTypes.pop(); return unionType; } - private void updateReadOnlyAndNullableFlag(BUnionType type) { + private void updateReadOnlyFlag(BUnionType type) { LinkedHashSet memberTypes = type.getMemberTypes(); LinkedHashSet flattenMemberTypes = new LinkedHashSet<>(memberTypes.size()); boolean isImmutable = true; - boolean hasNilableType = false; for (BType memBType : BUnionType.toFlatTypeSet(memberTypes)) { if (Types.getImpliedType(memBType).tag != TypeTags.NEVER) { flattenMemberTypes.add(memBType); } - if (isImmutable && !Symbols.isFlagOn(memBType.flags, Flags.READONLY)) { + if (isImmutable && !Symbols.isFlagOn(memBType.getFlags(), Flags.READONLY)) { isImmutable = false; } } if (isImmutable) { - type.flags |= Flags.READONLY; + type.addFlags(Flags.READONLY); if (type.tsymbol != null) { type.tsymbol.flags |= Flags.READONLY; } } - for (BType memberType : flattenMemberTypes) { - if (memberType.isNullable() && memberType.tag != TypeTags.NIL) { - hasNilableType = true; - break; - } - } - - if (hasNilableType) { - LinkedHashSet bTypes = new LinkedHashSet<>(flattenMemberTypes.size()); - for (BType t : flattenMemberTypes) { - if (t.tag != TypeTags.NIL) { - bTypes.add(t); - } - } - flattenMemberTypes = bTypes; - } - - for (BType memberType : flattenMemberTypes) { - if (memberType.isNullable()) { - type.setNullable(true); - } - } type.setOriginalMemberTypes(memberTypes); memberTypes.clear(); memberTypes.addAll(flattenMemberTypes); @@ -1396,7 +1375,7 @@ private BType resolveTypeDesc(BLangIntersectionTypeNode td, ResolverData data, b intersectionType.setConstituentTypes(constituentTypes); if (hasReadonly) { - intersectionType.flags |= Flags.READONLY; + intersectionType.addFlags(Flags.READONLY); } // Differ cyclic intersection between more than 2 non-readonly types. @@ -1427,7 +1406,7 @@ private void fillEffectiveType(BIntersectionType intersectionType, while (iterator.hasNext()) { BType bLangEffectiveImpliedType = Types.getImpliedType(effectiveType); if (bLangEffectiveImpliedType.tag == TypeTags.READONLY) { - intersectionType.flags = intersectionType.flags | TypeTags.READONLY; + intersectionType.addFlags(TypeTags.READONLY); effectiveType = iterator.next(); bLangEffectiveType = bLangTypeItr.next(); continue; @@ -1436,7 +1415,7 @@ private void fillEffectiveType(BIntersectionType intersectionType, BLangType bLangType = bLangTypeItr.next(); BType typeReferenceType = Types.getImpliedType(type); if (typeReferenceType.tag == TypeTags.READONLY) { - intersectionType.flags = intersectionType.flags | TypeTags.READONLY; + intersectionType.addFlags(TypeTags.READONLY); continue; } effectiveType = calculateEffectiveType(td, bLangEffectiveType, bLangType, effectiveType, type, @@ -1447,9 +1426,9 @@ private void fillEffectiveType(BIntersectionType intersectionType, } } intersectionType.effectiveType = effectiveType; - intersectionType.flags |= effectiveType.flags; + intersectionType.addFlags(effectiveType.getFlags()); - if ((intersectionType.flags & Flags.READONLY) == Flags.READONLY) { + if ((intersectionType.getFlags() & Flags.READONLY) == Flags.READONLY) { if (types.isInherentlyImmutableType(effectiveType)) { return; } @@ -1544,7 +1523,7 @@ private BType resolveTypeDesc(BLangUserDefinedType td, ResolverData data) { null, func.symbol, tempSymbol.pos, VIRTUAL); tSymbol.type = new BParameterizedType(paramValType, (BVarSymbol) tempSymbol, tSymbol, tempSymbol.name, parameterizedTypeInfo.index); - tSymbol.type.flags |= Flags.PARAMETERIZED; + tSymbol.type.addFlags(Flags.PARAMETERIZED); td.symbol = tSymbol; return tSymbol.type; @@ -1570,8 +1549,8 @@ private BType resolveTypeDesc(BLangUserDefinedType td, ResolverData data) { if (symbol.kind == SymbolKind.TYPE_DEF && !Symbols.isFlagOn(symbol.flags, Flags.ANONYMOUS)) { BType referenceType = ((BTypeDefinitionSymbol) symbol).referenceType; - referenceType.flags |= symbol.type.flags; - referenceType.tsymbol.flags |= symbol.type.flags; + referenceType.addFlags(symbol.type.getFlags()); + referenceType.tsymbol.flags |= symbol.type.getFlags(); return referenceType; } @@ -1605,8 +1584,8 @@ private BType resolveTypeDesc(BLangUserDefinedType td, ResolverData data) { td.symbol = symbol; if (symbol.kind == SymbolKind.TYPE_DEF && !Symbols.isFlagOn(symbol.flags, Flags.ANONYMOUS)) { BType referenceType = ((BTypeDefinitionSymbol) symbol).referenceType; - referenceType.flags |= symbol.type.flags; - referenceType.tsymbol.flags |= symbol.type.flags; + referenceType.addFlags(symbol.type.getFlags()); + referenceType.tsymbol.flags |= symbol.type.getFlags(); return referenceType; } return resolvedType; @@ -1624,48 +1603,41 @@ private BType resolveTypeDesc(BLangBuiltInRefTypeNode td, SymbolEnv symEnv) { return visitBuiltInTypeNode(td, data, td.typeKind); } - private BType resolveSingletonType(BLangFiniteTypeNode td, SymbolEnv symEnv) { + protected BType resolveSingletonType(BLangFiniteTypeNode td, SymbolEnv symEnv) { BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, (Flags.asMask(EnumSet.of(Flag.PUBLIC))), Names.EMPTY, symEnv.enclPkg.symbol.pkgID, null, symEnv.scope.owner, td.pos, BUILTIN); - // In case we encounter unary expressions in finite type, we will be replacing them with numeric literals. - replaceUnaryExprWithNumericLiteral(td); - - BFiniteType finiteType = new BFiniteType(finiteTypeSymbol); - for (BLangExpression literal : td.valueSpace) { - BType type = blangTypeUpdate(literal); + List vs = td.valueSpace; + SemNamedType[] valueSpace = new SemNamedType[vs.size()]; + for (int i = 0; i < vs.size(); i++) { + BLangExpression exprOrLiteral = vs.get(i); + BType type = blangTypeUpdate(exprOrLiteral); if (type != null && type.tag == TypeTags.SEMANTIC_ERROR) { return type; } if (type != null) { - literal.setBType(symTable.getTypeFromTag(type.tag)); + exprOrLiteral.setBType(symTable.getTypeFromTag(type.tag)); } - finiteType.addValue(literal); - } - finiteTypeSymbol.type = finiteType; - td.setBType(finiteType); - return finiteType; - } - private void replaceUnaryExprWithNumericLiteral(BLangFiniteTypeNode finiteTypeNode) { - List valueSpace = finiteTypeNode.valueSpace; - for (int i = 0; i < valueSpace.size(); i++) { - BLangExpression value; - NodeKind valueKind; - value = valueSpace.get(i); - valueKind = value.getKind(); - - if (valueKind == NodeKind.UNARY_EXPR) { - BLangUnaryExpr unaryExpr = (BLangUnaryExpr) value; - if (unaryExpr.expr.getKind() == NodeKind.NUMERIC_LITERAL) { - // Replacing unary expression with numeric literal type for + and - numeric values. - BLangNumericLiteral newNumericLiteral = - Types.constructNumericLiteralFromUnaryExpr(unaryExpr); - valueSpace.set(i, newNumericLiteral); + if (SemTypeHelper.isSimpleOrString(exprOrLiteral.getBType().getKind())) { + if (exprOrLiteral.getKind() == NodeKind.UNARY_EXPR) { + exprOrLiteral = Types.constructNumericLiteralFromUnaryExpr((BLangUnaryExpr) exprOrLiteral); + // Replacing here as Semantic Analyzer BLangFiniteTypeNode visit may not invoke for all finite nodes + td.valueSpace.set(i, exprOrLiteral); } + + SemType s = SemTypeHelper.resolveSingletonType((BLangLiteral) exprOrLiteral); + valueSpace[i] = new SemNamedType(s, Optional.ofNullable(exprOrLiteral.toString())); + } else { + throw new IllegalStateException("non-sem value found in BLangFiniteType!"); } } + + BFiniteType finiteType = new BFiniteType(finiteTypeSymbol, valueSpace); + finiteTypeSymbol.type = finiteType; + td.setBType(finiteType); + return finiteType; } private BType blangTypeUpdate(BLangExpression expression) { @@ -1707,7 +1679,7 @@ private BType resolveTypeDesc(BLangTableTypeNode td, ResolverData data) { SymbolEnv symEnv = data.env; BType type = resolveTypeDesc(symEnv, data.typeDefinition, data.depth + 1, td.type, data); - BTableType tableType = new BTableType(TypeTags.TABLE, symTable.empty, null); + BTableType tableType = new BTableType(symTable.typeEnv(), symTable.empty, null); BTypeSymbol typeSymbol = type.tsymbol; tableType.tsymbol = Symbols.createTypeSymbol(SymTag.TYPE, Flags.asMask(EnumSet.noneOf(Flag.class)), typeSymbol.name, typeSymbol.originalName, symEnv.enclPkg.symbol.pkgID, @@ -1763,7 +1735,7 @@ private BType resolveTypeDesc(BLangStreamType td, ResolverData data) { BType error = td.error != null ? resolveTypeDesc(symEnv, data.typeDefinition, data.depth + 1, td.error, data) : symTable.nilType; - BStreamType streamType = new BStreamType(TypeTags.STREAM, symTable.empty, error, null); + BStreamType streamType = new BStreamType(symTable.typeEnv(), TypeTags.STREAM, symTable.empty, error, null); BTypeSymbol typeSymbol = type.tsymbol; streamType.tsymbol = Symbols.createTypeSymbol(typeSymbol.tag, typeSymbol.flags, typeSymbol.name, typeSymbol.originalName, symEnv.enclPkg.symbol.pkgID, streamType, @@ -1822,7 +1794,7 @@ public BType defineTypeDefinition(BLangTypeDefinition typeDefinition, BType reso typeDefSymbol.pkgID, typeDefSymbol.type, typeDefSymbol.owner, typeDefSymbol.pos, typeDefSymbol.origin); typeSymbol.markdownDocumentation = typeDefSymbol.markdownDocumentation; ((BTypeDefinitionSymbol) typeDefSymbol).referenceType = new BTypeReferenceType(resolvedType, typeSymbol, - typeDefSymbol.type.flags); + typeDefSymbol.type.getFlags()); if (resolvedType == symTable.semanticError && resolvedType.tsymbol == null) { typeDefinition.symbol = typeDefSymbol; @@ -1894,7 +1866,7 @@ public BType defineTypeDefinition(BLangTypeDefinition typeDefinition, BType reso dlog.error(typeDefinition.pos, DiagnosticErrorCode.TYPE_PARAM_OUTSIDE_LANG_MODULE); } } - resolvedType.flags |= typeDefSymbol.flags; + resolvedType.addFlags(typeDefSymbol.flags); typeDefinition.symbol = typeDefSymbol; @@ -2011,12 +1983,6 @@ private void defineConstant(SymbolEnv symEnv, Map modTable, B BConstantSymbol constantSymbol = symEnter.getConstantSymbol(constant); constant.symbol = constantSymbol; BLangTypeDefinition typeDef = constant.associatedTypeDefinition; - NodeKind nodeKind = constant.expr.getKind(); - boolean isLiteral = nodeKind == NodeKind.LITERAL || nodeKind == NodeKind.NUMERIC_LITERAL - || nodeKind == NodeKind.UNARY_EXPR; - if (typeDef != null && isLiteral) { - resolveTypeDefinition(symEnv, modTable, typeDef, 0); - } if (constant.typeNode != null) { // Type node is available. ResolverData data = new ResolverData(); @@ -2036,6 +2002,17 @@ private void defineConstant(SymbolEnv symEnv, Map modTable, B BType resolvedType = constantTypeChecker.checkConstExpr(constant.expr, staticType, data); data.anonTypeNameSuffixes.pop(); + NodeKind nodeKind = constant.expr.getKind(); + boolean isLiteral = nodeKind == NodeKind.LITERAL || nodeKind == NodeKind.NUMERIC_LITERAL + || nodeKind == NodeKind.UNARY_EXPR; + if (typeDef != null && isLiteral) { + typeDef.typeNode.setBType(resolvedType); + // Define the typeDefinition. Add symbol, flags etc. + resolvedType = defineTypeDefinition(typeDef, resolvedType, symEnv); + typeDef.setBType(resolvedType); + typeDef.cycleDepth = -1; + } + if (resolvedType == symTable.semanticError) { // Constant expression contains errors. constant.setBType(symTable.semanticError); @@ -2059,7 +2036,7 @@ private void defineConstant(SymbolEnv symEnv, Map modTable, B // Update the final type in necessary fields. constantSymbol.type = intersectionType; if (intersectionType.tag == TypeTags.FINITE) { - constantSymbol.literalType = ((BFiniteType) intersectionType).getValueSpace().iterator().next().getBType(); + constantSymbol.literalType = singleShapeBroadType(intersectionType.semType(), symTable).get(); } else { constantSymbol.literalType = intersectionType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java index de443d69b5be..308347df0d2b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/Types.java @@ -20,7 +20,25 @@ import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.tools.diagnostics.DiagnosticCode; import io.ballerina.tools.diagnostics.Location; -import org.ballerinalang.model.Name; +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.Bdd; +import io.ballerina.types.CombinedRange; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.ListMemberTypes; +import io.ballerina.types.MappingAtomicType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypePair; +import io.ballerina.types.SemTypes; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -41,10 +59,7 @@ import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BObjectTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BRecordTypeSymbol; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BResourceFunction; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BResourcePathSegmentSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BStructureTypeSymbol; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.SymTag; @@ -52,20 +67,16 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BField; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; import org.wso2.ballerinalang.compiler.semantics.model.types.BIntersectionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BParameterizedType; import org.wso2.ballerinalang.compiler.semantics.model.types.BReadonlyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BRecordType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BSequenceType; import org.wso2.ballerinalang.compiler.semantics.model.types.BStreamType; import org.wso2.ballerinalang.compiler.semantics.model.types.BTableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BTupleMember; @@ -73,10 +84,9 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BType; import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeIdSet; import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeReferenceType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypeVisitor; -import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; +import org.wso2.ballerinalang.compiler.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.tree.BLangFunction; import org.wso2.ballerinalang.compiler.tree.BLangNode; import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; @@ -102,13 +112,12 @@ import org.wso2.ballerinalang.compiler.tree.types.BLangRecordTypeNode; import org.wso2.ballerinalang.compiler.util.BArrayState; import org.wso2.ballerinalang.compiler.util.CompilerContext; -import org.wso2.ballerinalang.compiler.util.CompilerUtils; import org.wso2.ballerinalang.compiler.util.ImmutableTypeCloner; +import org.wso2.ballerinalang.compiler.util.Name; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.NumericLiteralSupport; import org.wso2.ballerinalang.compiler.util.TypeDefBuilderHelper; import org.wso2.ballerinalang.compiler.util.TypeTags; -import org.wso2.ballerinalang.compiler.util.Unifier; import org.wso2.ballerinalang.util.Flags; import org.wso2.ballerinalang.util.Lists; @@ -133,6 +142,11 @@ import java.util.Set; import static io.ballerina.runtime.api.constants.RuntimeConstants.UNDERSCORE; +import static io.ballerina.types.BasicTypeCode.BT_OBJECT; +import static io.ballerina.types.Core.combineRanges; +import static io.ballerina.types.Core.createIsolatedObject; +import static io.ballerina.types.Core.createServiceObject; +import static io.ballerina.types.Core.isSubtypeSimple; import static org.ballerinalang.model.symbols.SymbolOrigin.SOURCE; import static org.ballerinalang.model.symbols.SymbolOrigin.VIRTUAL; import static org.wso2.ballerinalang.compiler.semantics.model.SymbolTable.BBYTE_MAX_VALUE; @@ -146,11 +160,9 @@ import static org.wso2.ballerinalang.compiler.semantics.model.SymbolTable.UNSIGNED16_MAX_VALUE; import static org.wso2.ballerinalang.compiler.semantics.model.SymbolTable.UNSIGNED32_MAX_VALUE; import static org.wso2.ballerinalang.compiler.semantics.model.SymbolTable.UNSIGNED8_MAX_VALUE; -import static org.wso2.ballerinalang.compiler.util.TypeTags.NEVER; import static org.wso2.ballerinalang.compiler.util.TypeTags.OBJECT; import static org.wso2.ballerinalang.compiler.util.TypeTags.RECORD; import static org.wso2.ballerinalang.compiler.util.TypeTags.UNION; -import static org.wso2.ballerinalang.compiler.util.TypeTags.isSimpleBasicType; /** * This class consists of utility methods which operate on types. @@ -163,17 +175,15 @@ public class Types { private static final CompilerContext.Key TYPES_KEY = new CompilerContext.Key<>(); - private final Unifier unifier; private final SymbolTable symTable; private final SymbolResolver symResolver; private final BLangDiagnosticLog dlog; private final Names names; private int finiteTypeCount = 0; - private final BUnionType expandedXMLBuiltinSubtypes; private final BLangAnonymousModelHelper anonymousModelHelper; - private final int recordCount = 0; private SymbolEnv env; - private boolean ignoreObjectTypeIds = false; + protected final Context semTypeCtx; + private static final String BASE_16 = "base16"; private static final BigDecimal DECIMAL_MAX = @@ -185,6 +195,8 @@ public class Types { private static final BigDecimal MIN_DECIMAL_MAGNITUDE = new BigDecimal("1.000000000000000000000000000000000e-6143", MathContext.DECIMAL128); + public static final String AND_READONLY_SUFFIX = " & readonly"; + public static Types getInstance(CompilerContext context) { Types types = context.get(TYPES_KEY); if (types == null) { @@ -195,29 +207,20 @@ public static Types getInstance(CompilerContext context) { } public Types(CompilerContext context) { + this(context, new Env()); + } + + public Types(CompilerContext context, Env typeEnv) { context.put(TYPES_KEY, this); + this.semTypeCtx = Context.from(typeEnv); this.symTable = SymbolTable.getInstance(context); this.symResolver = SymbolResolver.getInstance(context); this.dlog = BLangDiagnosticLog.getInstance(context); this.names = Names.getInstance(context); - this.expandedXMLBuiltinSubtypes = BUnionType.create(null, - symTable.xmlElementType, symTable.xmlCommentType, - symTable.xmlPIType, symTable.xmlTextType); - this.unifier = new Unifier(); this.anonymousModelHelper = BLangAnonymousModelHelper.getInstance(context); } - public List checkTypes(BLangExpression node, - List actualTypes, - List expTypes) { - List resTypes = new ArrayList<>(); - for (int i = 0; i < actualTypes.size(); i++) { - resTypes.add(checkType(node, actualTypes.get(i), expTypes.size() > i ? expTypes.get(i) : symTable.noType)); - } - return resTypes; - } - public BType checkType(BLangExpression node, BType actualType, BType expType) { @@ -230,7 +233,7 @@ public BType addNilForNillableAccessType(BType actualType) { return actualType; } - return BUnionType.create(null, actualType, symTable.nilType); + return BUnionType.create(typeEnv(), null, actualType, symTable.nilType); } public BType checkType(BLangExpression expr, @@ -254,6 +257,10 @@ public boolean typeIncompatible(Location pos, BType actualType, BType expType) { return checkType(pos, actualType, expType, DiagnosticErrorCode.INCOMPATIBLE_TYPES) == symTable.semanticError; } + public SemType getErrorIntersection(SemType t) { + return SemTypes.intersect(t, PredefinedType.ERROR); + } + public BType getErrorTypes(BType bType) { bType = Types.getImpliedType(bType); if (bType == null) { @@ -289,7 +296,8 @@ public BType getErrorTypes(BType bType) { return errorType; } - return errTypes.size() == 1 ? errTypes.iterator().next() : BUnionType.create(null, errTypes); + return errTypes.size() == 1 ? errTypes.iterator().next() : + BUnionType.create(typeEnv(), null, errTypes); } public BType checkType(Location pos, @@ -312,131 +320,100 @@ public BType checkType(Location pos, } public boolean isLaxFieldAccessAllowed(BType type) { - type = Types.getImpliedType(type); - Set visited = new HashSet<>(); - return isLaxType(type, visited) == 1 || isAssignable(type, symTable.xmlType); + if (type.tag == TypeTags.SEMANTIC_ERROR) { + return false; + } + return isLaxFieldAccessAllowed(type.semType()); } - // TODO : clean - public int isLaxType(BType type, Set visited) { - type = getImpliedType(type); - if (!visited.add(type)) { - return -1; - } - switch (type.tag) { - case TypeTags.JSON: - return 1; - case TypeTags.MAP: - return isLaxType(((BMapType) type).constraint, visited); - case TypeTags.UNION: - if (isSameType(type, symTable.jsonType)) { - visited.add(type); - return 1; - } - boolean atleastOneLaxType = false; - for (BType member : ((BUnionType) type).getMemberTypes()) { - int result = isLaxType(member, visited); - if (result == -1) { - continue; - } - if (result == 0) { - return 0; - } - atleastOneLaxType = true; - } - return atleastOneLaxType ? 1 : 0; + public boolean isLaxFieldAccessAllowed(SemType t) { + if (Core.isNever(t)) { + return false; } - return 0; + return isSubtypeSimple(t, PredefinedType.XML) || isLaxType(t); } - public boolean isLaxType(BType type, Map visited) { - type = getImpliedType(type); - if (visited.containsKey(type)) { - return visited.get(type); + /** + * Checks if the type is a lax type. + *

+ * Rules: + *

    + *
  • json and readonly-json are lax
  • + *
  • map<T> is lax if T is lax
  • + *
  • U = T1|T2...|Tn is lax, if Ti is lax for all i.
  • + *
+ * + * @param t type to be checked + * @return true if t is lax + */ + private boolean isLaxType(SemType t) { + SemType json = Core.createJson(semTypeCtx); + if (SemTypes.isSameType(semTypeCtx, t, json) || + SemTypes.isSameType(semTypeCtx, t, SemTypes.intersect(json, PredefinedType.VAL_READONLY))) { + return true; } - switch (type.tag) { - case TypeTags.JSON: - visited.put(type, true); - return true; - case TypeTags.MAP: - boolean result = isLaxType(((BMapType) type).constraint, visited); - visited.put(type, result); - return result; - case TypeTags.UNION: - // TODO: remove - if (type == symTable.jsonType || isSameType(type, symTable.jsonType)) { - visited.put(type, true); - return true; - } - for (BType member : ((BUnionType) type).getMemberTypes()) { - if (!isLaxType(member, visited)) { - visited.put(type, false); - return false; - } - } - visited.put(type, true); - return true; - } - visited.put(type, false); - return false; - } - public boolean isSameType(BType source, BType target) { - return isSameType(source, target, new HashSet<>()); - } + Optional> optMatList = Core.mappingAtomicTypesInUnion(semTypeCtx, t); + if (optMatList.isEmpty()) { + return false; + } - public boolean isSameOrderedType(BType source, BType target) { - return isSameOrderedType(source, target, new HashSet<>()); + List matList = optMatList.get(); + return matList.stream().allMatch(mat -> mat.names().length == 0 && isLaxType(Core.cellInnerVal(mat.rest()))); } - private boolean isSameOrderedType(BType source, BType target, Set unresolvedTypes) { - source = getImpliedType(source); - target = getImpliedType(target); - if (isNil(source) || isNil(target)) { - // If type T is ordered, then type T? Is also ordered. - // Both source and target are ordered types since they were checked in previous stage. - // Ex. Let take target -> T, source -> (). T? is ordered type where the static type of both operands belong. - return true; - } - if (!unresolvedTypes.add(new TypePair(source, target))) { - return true; + boolean isUniqueType(Iterable typeList, BType type) { + type = Types.getImpliedType(type); + boolean isRecord = type.tag == TypeTags.RECORD; + + for (BType bType : typeList) { + bType = Types.getImpliedType(bType); + if (isRecord) { + // Seems defaultable values too are considered when checking uniqueness. + if (type == bType) { + return false; + } + } else if (isSameType(type, bType)) { + return false; + } } - BTypeVisitor orderedTypeVisitor = new BOrderedTypeVisitor(unresolvedTypes); - return target.accept(orderedTypeVisitor, source); + return true; } - public boolean isPureType(BType type) { - IsPureTypeUniqueVisitor visitor = new IsPureTypeUniqueVisitor(); - return visitor.visit(type); + public boolean isSameType(BType source, BType target) { + return isSameType(source.semType(), target.semType()); } - public boolean isAnydata(BType type) { - IsAnydataUniqueVisitor visitor = new IsAnydataUniqueVisitor(); - return visitor.visit(type); - } + public boolean isSameTypeIncludingTags(BType source, BType target) { + if (source.tag != target.tag) { + return false; + } - private boolean isSameType(BType source, BType target, Set unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = null; - if (!isValueType(source) && !isValueType(target)) { - pair = new TypePair(source, target); - if (!unresolvedTypes.add(pair)) { - return true; + if (source.tag == UNION) { + boolean notSameType = ((BUnionType) source).getMemberTypes() + .stream() + .map(sT -> ((BUnionType) target).getMemberTypes() + .stream() + .anyMatch(it -> Types.getReferredType(it).tag == Types.getReferredType(sT).tag)) + .anyMatch(foundSameType -> !foundSameType); + if (notSameType) { + return false; } } - BTypeVisitor sameTypeVisitor = new BSameTypeVisitor(unresolvedTypes, this::isSameType); + return isSameType(source.semType(), target.semType()); + } - if (target.accept(sameTypeVisitor, source)) { - return true; - } + public boolean isSameType(SemType source, SemType target) { + return SemTypes.isSameType(semTypeCtx, source, target); + } - if (pair != null) { - unresolvedTypes.remove(pair); - } + public SemType anydata() { + return Core.createAnydata(semTypeCtx); + } - return false; + public boolean isAnydata(SemType t) { + return isSubtype(t, anydata()); } public boolean isValueType(BType type) { @@ -463,49 +440,16 @@ boolean isBasicNumericType(BType bType) { return type.tag < TypeTags.STRING || TypeTags.isIntegerTypeTag(type.tag); } - boolean finiteTypeContainsNumericTypeValues(BFiniteType finiteType) { - return finiteType.getValueSpace().stream().anyMatch(valueExpr -> isBasicNumericType(valueExpr.getBType())); - } - public boolean containsErrorType(BType bType) { - BType type = getImpliedType(bType); - if (type.tag == TypeTags.UNION) { - return ((BUnionType) type).getMemberTypes().stream() - .anyMatch(this::containsErrorType); - } - - if (type.tag == TypeTags.READONLY) { - return true; - } - - return type.tag == TypeTags.ERROR; + return SemTypeHelper.containsBasicType(bType, PredefinedType.ERROR); } public boolean containsNilType(BType bType) { - BType type = getImpliedType(bType); - if (type.tag == TypeTags.UNION) { - for (BType memberType : ((BUnionType) type).getMemberTypes()) { - if (containsNilType(memberType)) { - return true; - } - } - return false; - } - - if (type.tag == TypeTags.READONLY) { - return true; - } - - return type.tag == TypeTags.NIL; + return SemTypeHelper.containsBasicType(bType, PredefinedType.NIL); } public boolean isSubTypeOfList(BType bType) { - BType type = getImpliedType(bType); - if (type.tag != TypeTags.UNION) { - return isSubTypeOfBaseType(type, TypeTags.ARRAY) || isSubTypeOfBaseType(type, TypeTags.TUPLE); - } - - return ((BUnionType) type).getMemberTypes().stream().allMatch(this::isSubTypeOfList); + return SemTypeHelper.isSubtypeSimpleNotNever(bType, PredefinedType.LIST); } BType resolvePatternTypeFromMatchExpr(BLangErrorBindingPattern errorBindingPattern, BLangExpression matchExpr, @@ -736,31 +680,11 @@ public BType resolvePatternTypeFromMatchExpr(BLangMappingBindingPattern mappingB } private boolean containsAnyType(BType type) { - type = getImpliedType(type); - if (type.tag != TypeTags.UNION) { - return type.tag == TypeTags.ANY; - } - - for (BType memberTypes : ((BUnionType) type).getMemberTypes()) { - if (getImpliedType(memberTypes).tag == TypeTags.ANY) { - return true; - } - } - return false; + return SemTypeHelper.containsType(semTypeCtx, type, PredefinedType.ANY); } private boolean containsAnyDataType(BType type) { - type = getImpliedType(type); - if (type.tag != TypeTags.UNION) { - return type.tag == TypeTags.ANYDATA; - } - - for (BType memberTypes : ((BUnionType) type).getMemberTypes()) { - if (getImpliedType(memberTypes).tag == TypeTags.ANYDATA) { - return true; - } - } - return false; + return SemTypeHelper.containsType(semTypeCtx, type, Core.createAnydata(semTypeCtx)); } BType mergeTypes(BType typeFirst, BType typeSecond) { @@ -779,17 +703,21 @@ BType mergeTypes(BType typeFirst, BType typeSecond) { if (isSameBasicType(typeFirst, typeSecond)) { return typeFirst; } - return BUnionType.create(null, typeFirst, typeSecond); + return BUnionType.create(typeEnv(), null, typeFirst, typeSecond); } - public boolean isSubTypeOfMapping(BType bType) { - BType type = getImpliedType(bType); - if (type.tag != TypeTags.UNION) { - return isSubTypeOfBaseType(type, TypeTags.MAP) || isSubTypeOfBaseType(type, TypeTags.RECORD); - } - return ((BUnionType) type).getMemberTypes().stream().allMatch(this::isSubTypeOfMapping); + public boolean isSubTypeOfMapping(SemType s) { + return SemTypes.isSubtypeSimpleNotNever(s, PredefinedType.MAPPING); + } + + public boolean isSubTypeOfBaseType(BType bType, BasicTypeBitSet bbs) { + return SemTypeHelper.isSubtypeSimpleNotNever(bType, bbs); } + /** + * @deprecated Use {@link #isSubTypeOfBaseType(BType, BasicTypeBitSet)} instead. + */ + @Deprecated public boolean isSubTypeOfBaseType(BType bType, int baseTypeTag) { BType type = getImpliedType(bType); @@ -828,3032 +756,1027 @@ private boolean isUnionMemberTypesSubTypeOfBaseType(LinkedHashSet memberT /** * Checks whether source type is assignable to the target type. - *

- * Source type is assignable to the target type if, - * 1) the target type is any and the source type is not a value type. - * 2) there exists an implicit cast symbol from source to target. - * 3) both types are JSON and the target constraint is no type. - * 4) both types are array type and both array types are assignable. - * 5) both types are MAP and the target constraint is any type or constraints are structurally equivalent. * * @param source type. * @param target type. * @return true if source type is assignable to the target type. */ public boolean isAssignable(BType source, BType target) { - return isAssignable(source, target, new HashSet<>()); + return isSubtype(source.semType(), target.semType()); } public boolean isAssignableIgnoreObjectTypeIds(BType source, BType target) { - this.ignoreObjectTypeIds = true; - boolean result = isAssignable(source, target); - this.ignoreObjectTypeIds = false; - return result; + SemType s = typeIgnoringObjectTypeIds(source.semType()); + SemType t = typeIgnoringObjectTypeIds(target.semType()); + return isSubtype(s, t); } - private boolean isAssignable(BType source, BType target, Set unresolvedTypes) { - - if (isSameType(source, target)) { - return true; - } - - int sourceTag = source.tag; - int targetTag = target.tag; - - if (sourceTag == TypeTags.TYPEREFDESC || targetTag == TypeTags.TYPEREFDESC) { - return isAssignable(getImpliedType(source), getImpliedType(target), - unresolvedTypes); + public SemType typeIgnoringObjectTypeIds(SemType t) { + SubtypeData objSubTypeData = Core.subtypeData(t, BT_OBJECT); + if (!(objSubTypeData instanceof Bdd b)) { + return t; } + Bdd bdd = replaceObjectDistinctAtoms(b); + SemType newObjSemType = Core.createBasicSemType(BT_OBJECT, bdd); + SemType diff = Core.diff(t, PredefinedType.OBJECT); + return Core.union(diff, newObjSemType); + } - if (isNeverTypeOrStructureTypeWithARequiredNeverMember(source)) { - return true; + /** + * Replaces all distinct atoms in object type's bdd with full object equivalent atom. + * ({@link PredefinedType#ATOM_MAPPING_OBJECT}). + *
+ * This is to suppress effect coming from distinct atoms. + * The return bdd will be equivalent to object bdd with no distinct atoms. + * + * @param b a bdd belong to object type + * @return bdd with no distinct atoms + */ + private Bdd replaceObjectDistinctAtoms(Bdd b) { + if (b instanceof BddAllOrNothing) { + return b; } - if (sourceTag == TypeTags.SEQUENCE) { - BSequenceType seqType = (BSequenceType) source; - if (targetTag == TypeTags.ARRAY) { - return isAssignable(seqType.elementType, ((BArrayType) target).eType, unresolvedTypes); - } + BddNode bn = (BddNode) b; + Atom atom = bn.atom(); + if (bn.atom().kind() == Atom.Kind.DISTINCT_ATOM) { + atom = PredefinedType.ATOM_MAPPING_OBJECT; } + Bdd left = replaceObjectDistinctAtoms(bn.left()); + Bdd middle = replaceObjectDistinctAtoms(bn.middle()); + Bdd right = replaceObjectDistinctAtoms(bn.right()); + return BddNode.create(atom, left, middle, right); + } - if (!Symbols.isFlagOn(source.flags, Flags.PARAMETERIZED) && - !isInherentlyImmutableType(target) && Symbols.isFlagOn(target.flags, Flags.READONLY) && - !isInherentlyImmutableType(source) && isMutable(source)) { - return false; - } + public boolean isSubtype(SemType t1, SemType t2) { + return SemTypes.isSubtype(semTypeCtx, t1, t2); + } - if (sourceTag == TypeTags.INTERSECTION) { - return isAssignable(((BIntersectionType) source).effectiveType, - targetTag != TypeTags.INTERSECTION ? target : - ((BIntersectionType) target).effectiveType, unresolvedTypes); - } + public boolean isSubtype(BType t1, SemType t2) { + return SemTypeHelper.isSubtype(semTypeCtx, t1, t2); + } - if (targetTag == TypeTags.INTERSECTION) { - return isAssignable(source, ((BIntersectionType) target).effectiveType, unresolvedTypes); - } + BField getTableConstraintField(BType constraintType, String fieldName) { + constraintType = getImpliedType(constraintType); + switch (constraintType.tag) { + case TypeTags.RECORD: + Map fieldList = ((BRecordType) constraintType).getFields(); + return fieldList.get(fieldName); + case TypeTags.UNION: + BUnionType unionType = (BUnionType) constraintType; + Set memTypes = unionType.getMemberTypes(); + List fields = memTypes.stream().map(type -> getTableConstraintField(type, fieldName)) + .filter(Objects::nonNull).toList(); - if (sourceTag == TypeTags.PARAMETERIZED_TYPE) { - return isParameterizedTypeAssignable(source, target, unresolvedTypes); - } + if (fields.size() != memTypes.size()) { + return null; + } - if (sourceTag == TypeTags.BYTE && targetTag == TypeTags.INT) { - return true; + if (fields.stream().allMatch(field -> isAssignable(field.type, fields.get(0).type) && + isAssignable(fields.get(0).type, field.type))) { + return fields.get(0); + } } - if (TypeTags.isXMLTypeTag(sourceTag) && TypeTags.isXMLTypeTag(targetTag)) { - return isXMLTypeAssignable(source, target, unresolvedTypes); - } + return null; + } - if (sourceTag == TypeTags.CHAR_STRING && targetTag == TypeTags.STRING) { + public boolean isInherentlyImmutableType(BType type) { + type = getImpliedType(type); + if (isValueType(type)) { return true; } - if (sourceTag == TypeTags.ERROR && targetTag == TypeTags.ERROR) { - return isErrorTypeAssignable((BErrorType) source, (BErrorType) target, unresolvedTypes); - } else if (sourceTag == TypeTags.ERROR && targetTag == TypeTags.ANY) { - return false; - } - - if (sourceTag == TypeTags.NIL && (isNullable(target) || targetTag == TypeTags.JSON)) { - return true; - } + return switch (type.tag) { + case TypeTags.XML_TEXT, + TypeTags.FINITE, // Assuming a finite type will only have members from simple basic types. + TypeTags.READONLY, + TypeTags.NIL, + TypeTags.NEVER, + TypeTags.ERROR, + TypeTags.INVOKABLE, + TypeTags.TYPEDESC, + TypeTags.HANDLE, + TypeTags.REGEXP -> true; + case TypeTags.XML -> getImpliedType(((BXMLType) type).constraint).tag == TypeTags.NEVER; + default -> false; + }; + } - // TODO: Remove the isValueType() check - if (targetTag == TypeTags.ANY && !containsErrorType(source) && !isValueType(source)) { - return true; + /** + * Retrieve the referred type if a given type is a type reference type or + * retrieve the effective type if the given type is an intersection type. + * + * @param type type to retrieve the implied type + * @return the implied type if provided with a type reference type or an intersection type, + * else returns the original type + */ + public static BType getImpliedType(BType type) { + type = getReferredType(type); + if (type != null && type.tag == TypeTags.INTERSECTION) { + return getImpliedType(((BIntersectionType) type).effectiveType); } - if (targetTag == TypeTags.ANYDATA && !containsErrorType(source) && isAnydata(source)) { - return true; - } + return type; + } - if (targetTag == TypeTags.READONLY) { - if ((isInherentlyImmutableType(source) || Symbols.isFlagOn(source.flags, Flags.READONLY))) { - return true; - } - if (isAssignable(source, symTable.anyAndReadonlyOrError, unresolvedTypes)) { - return true; - } + public static BType getReferredType(BType type) { + if (type != null && type.tag == TypeTags.TYPEREFDESC) { + return getReferredType(((BTypeReferenceType) type).referredType); } - if (sourceTag == TypeTags.READONLY && isAssignable(symTable.anyAndReadonlyOrError, target, unresolvedTypes)) { - return true; - } + return type; + } - if (targetTag == TypeTags.MAP && sourceTag == TypeTags.RECORD) { - BRecordType recordType = (BRecordType) source; - return isAssignableRecordType(recordType, target, unresolvedTypes); + public BLangExpression addConversionExprIfRequired(BLangExpression expr, BType lhsType) { + if (lhsType.tag == TypeTags.NONE) { + return expr; } - if (targetTag == TypeTags.RECORD && sourceTag == TypeTags.MAP) { - return isAssignableMapType((BMapType) source, (BRecordType) target); - } + BType rhsType = expr.getBType(); - if (targetTag == TypeTags.TYPEDESC && sourceTag == TypeTags.TYPEDESC) { - return isAssignable(((BTypedescType) source).constraint, (((BTypedescType) target).constraint), - unresolvedTypes); + if (lhsType.tag == TypeTags.TYPEREFDESC && rhsType.tag != TypeTags.TYPEREFDESC) { + return addConversionExprIfRequired(expr, Types.getReferredType(lhsType)); } - if (targetTag == TypeTags.TABLE && sourceTag == TypeTags.TABLE) { - return isAssignableTableType((BTableType) source, (BTableType) target, unresolvedTypes); + if (rhsType.tag == lhsType.tag && isSameType(rhsType, lhsType)) { + return expr; } - if (targetTag == TypeTags.STREAM && sourceTag == TypeTags.STREAM) { - return isAssignableStreamType((BStreamType) source, (BStreamType) target, unresolvedTypes); + setImplicitCastExpr(expr, rhsType, lhsType); + if (expr.impConversionExpr != null) { + BLangExpression impConversionExpr = expr.impConversionExpr; + expr.impConversionExpr = null; + return impConversionExpr; } - if (isBuiltInTypeWidenPossible(source, target) == TypeTestResult.TRUE) { - return true; + if (lhsType.tag == TypeTags.JSON && rhsType.tag == TypeTags.NIL) { + return expr; } - if (sourceTag == TypeTags.FINITE) { - return isFiniteTypeAssignable((BFiniteType) source, target, unresolvedTypes); + if (lhsType.tag == TypeTags.NIL && rhsType.isNullable()) { + return expr; } - if ((targetTag == TypeTags.UNION || sourceTag == TypeTags.UNION) && - isAssignableToUnionType(source, target, unresolvedTypes)) { - return true; + if (lhsType.tag == TypeTags.ARRAY && rhsType.tag == TypeTags.TUPLE) { + return expr; } - if (targetTag == TypeTags.JSON) { - if (sourceTag == TypeTags.JSON) { - return true; - } + // Create a type cast expression + BLangTypeConversionExpr conversionExpr = (BLangTypeConversionExpr) + TreeBuilder.createTypeConversionNode(); + conversionExpr.expr = expr; + conversionExpr.targetType = lhsType; + conversionExpr.setBType(lhsType); + conversionExpr.pos = expr.pos; + conversionExpr.checkTypes = false; + conversionExpr.internal = true; + return conversionExpr; + } - if (sourceTag == TypeTags.TUPLE) { - return isTupleTypeAssignable(source, target, unresolvedTypes); - } + boolean isSelectivelyImmutableType(BType type, PackageID packageID) { + return isSelectivelyImmutableType(type, new HashSet<>(), false, packageID); + } - if (sourceTag == TypeTags.ARRAY) { - return isArrayTypesAssignable((BArrayType) source, target, unresolvedTypes); - } - - if (sourceTag == TypeTags.MAP) { - return isAssignable(((BMapType) source).constraint, target, unresolvedTypes); - } - - if (sourceTag == TypeTags.RECORD) { - return isAssignableRecordType((BRecordType) source, target, unresolvedTypes); - } - } - - if (targetTag == TypeTags.FUTURE && sourceTag == TypeTags.FUTURE) { - if (((BFutureType) target).constraint.tag == TypeTags.NONE) { - return true; - } - return isAssignable(((BFutureType) source).constraint, ((BFutureType) target).constraint, unresolvedTypes); - } - - if (targetTag == TypeTags.MAP && sourceTag == TypeTags.MAP) { - // Here source condition is added for prevent assigning map union constrained - // to map any constrained. - if (((BMapType) target).constraint.tag == TypeTags.ANY && - ((BMapType) source).constraint.tag != TypeTags.UNION) { - return true; - } - - return isAssignable(((BMapType) source).constraint, ((BMapType) target).constraint, unresolvedTypes); - } - - if ((sourceTag == TypeTags.OBJECT || sourceTag == TypeTags.RECORD) - && (targetTag == TypeTags.OBJECT || targetTag == TypeTags.RECORD)) { - return checkStructEquivalency(source, target, unresolvedTypes); - } - - if (sourceTag == TypeTags.TUPLE && targetTag == TypeTags.ARRAY) { - return isTupleTypeAssignableToArrayType((BTupleType) source, (BArrayType) target, unresolvedTypes); - } - - if (sourceTag == TypeTags.ARRAY && targetTag == TypeTags.TUPLE) { - return isArrayTypeAssignableToTupleType((BArrayType) source, (BTupleType) target, unresolvedTypes); - } - - if (sourceTag == TypeTags.TUPLE || targetTag == TypeTags.TUPLE) { - return isTupleTypeAssignable(source, target, unresolvedTypes); - } - - if (sourceTag == TypeTags.INVOKABLE && targetTag == TypeTags.INVOKABLE) { - return isFunctionTypeAssignable((BInvokableType) source, (BInvokableType) target, new HashSet<>()); - } - - return (sourceTag == TypeTags.ARRAY || sourceTag == TypeTags.BYTE_ARRAY) - && targetTag == TypeTags.ARRAY - && isArrayTypesAssignable((BArrayType) source, target, unresolvedTypes); + boolean isSelectivelyImmutableType(BType type, boolean forceCheck, PackageID packageID) { + return isSelectivelyImmutableType(type, new HashSet<>(), forceCheck, packageID); } - private boolean isMutable(BType type) { - if (Symbols.isFlagOn(type.flags, Flags.READONLY)) { - return false; - } - - type = getImpliedType(type); - if (type.tag != TypeTags.UNION) { - return true; - } - - BUnionType unionType = (BUnionType) type; - for (BType memberType : unionType.getMemberTypes()) { - if (!Symbols.isFlagOn(memberType.flags, Flags.READONLY)) { - return true; - } - } - - unionType.flags |= Flags.READONLY; - BTypeSymbol tsymbol = unionType.tsymbol; - if (tsymbol != null) { - tsymbol.flags |= Flags.READONLY; - } - return false; + public boolean isSelectivelyImmutableType(BType type, Set unresolvedTypes, PackageID packageID) { + return isSelectivelyImmutableType(type, unresolvedTypes, false, packageID); } - private boolean isParameterizedTypeAssignable(BType source, BType target, Set unresolvedTypes) { - BType resolvedSourceType = unifier.build(source); - target = getImpliedType(target); - if (target.tag != TypeTags.PARAMETERIZED_TYPE) { - return isAssignable(resolvedSourceType, target, unresolvedTypes); - } + private boolean isSelectivelyImmutableType(BType input, Set unresolvedTypes, boolean forceCheck, + PackageID packageID) { + BType type = getImpliedType(input); - if (((BParameterizedType) source).paramIndex != ((BParameterizedType) target).paramIndex) { + if (isInherentlyImmutableType(type) || !(type instanceof SelectivelyImmutableReferenceType)) { + // Always immutable. return false; } - return isAssignable(resolvedSourceType, unifier.build(target), unresolvedTypes); - } - - private boolean isAssignableRecordType(BRecordType recordType, BType type, Set unresolvedTypes) { - TypePair pair = new TypePair(recordType, type); - if (!unresolvedTypes.add(pair)) { + if (!unresolvedTypes.add(type)) { return true; } - type = getImpliedType(type); - BType targetType = switch (type.tag) { - case TypeTags.MAP -> ((BMapType) type).constraint; - case TypeTags.JSON -> type; - default -> throw new IllegalArgumentException("Incompatible target type: " + type); - }; - return recordFieldsAssignableToType(recordType, targetType, unresolvedTypes); - } - - private boolean isAssignableStreamType(BStreamType sourceStreamType, BStreamType targetStreamType, - Set unresolvedTypes) { - return isAssignable(sourceStreamType.constraint, targetStreamType.constraint, unresolvedTypes) - && isAssignable(sourceStreamType.completionType, targetStreamType.completionType, unresolvedTypes); - } - - private boolean recordFieldsAssignableToType(BRecordType recordType, BType targetType, - Set unresolvedTypes) { - for (BField field : recordType.fields.values()) { - if (!isAssignable(field.type, targetType, unresolvedTypes)) { - return false; - } - } - - if (!recordType.sealed) { - return isAssignable(recordType.restFieldType, targetType, unresolvedTypes); - } - - return true; - } - - private boolean isAssignableTableType(BTableType sourceTableType, BTableType targetTableType, - Set unresolvedTypes) { - if (!isAssignable(sourceTableType.constraint, targetTableType.constraint, unresolvedTypes)) { - return false; - } - - if (targetTableType.keyTypeConstraint == null && targetTableType.fieldNameList.isEmpty()) { + if (!forceCheck && + getImmutableType(symTable, packageID, (SelectivelyImmutableReferenceType) type).isPresent()) { return true; } - if (targetTableType.keyTypeConstraint != null) { - if (sourceTableType.keyTypeConstraint != null && - (isAssignable(sourceTableType.keyTypeConstraint, targetTableType.keyTypeConstraint, - unresolvedTypes))) { + switch (type.tag) { + case TypeTags.ANY: + case TypeTags.ANYDATA: + case TypeTags.JSON: + case TypeTags.XML: + case TypeTags.XML_COMMENT: + case TypeTags.XML_ELEMENT: + case TypeTags.XML_PI: + case TypeTags.BYTE_ARRAY: return true; - } - - if (sourceTableType.fieldNameList.isEmpty()) { - return false; - } - - List fieldTypes = new ArrayList<>(); - sourceTableType.fieldNameList.stream() - .map(f -> getTableConstraintField(sourceTableType.constraint, f)) - .filter(Objects::nonNull).map(f -> new BTupleMember(f.type, - Symbols.createVarSymbolForTupleMember(f.type))).forEach(fieldTypes::add); - if (fieldTypes.size() == 1) { - return isAssignable(fieldTypes.get(0).type, targetTableType.keyTypeConstraint, unresolvedTypes); - } - - BTupleType tupleType = new BTupleType(fieldTypes); - return isAssignable(tupleType, targetTableType.keyTypeConstraint, unresolvedTypes); - } - - return targetTableType.fieldNameList.equals(sourceTableType.fieldNameList); - } + case TypeTags.ARRAY: + BArrayType arrayType = (BArrayType) type; + BType elementType = arrayType.eType; + if (elementType == symTable.semanticError && arrayType.mutableType != null) { + elementType = arrayType.mutableType.eType; + } + return isInherentlyImmutableType(elementType) || + isSelectivelyImmutableType(elementType, unresolvedTypes, forceCheck, packageID); + case TypeTags.TUPLE: + BTupleType tupleType = (BTupleType) type; + List tupleMemberTypes = tupleType.getTupleTypes(); + if (tupleMemberTypes.isEmpty() && tupleType.mutableType != null) { + tupleMemberTypes = tupleType.mutableType.getTupleTypes(); + } + for (BType memberType : tupleMemberTypes) { + if (!isInherentlyImmutableType(memberType) && + !isSelectivelyImmutableType(memberType, unresolvedTypes, forceCheck, packageID)) { + return false; + } + } + BType tupRestType = tupleType.restType; + if (tupRestType == null) { + return true; + } - BField getTableConstraintField(BType constraintType, String fieldName) { - constraintType = getImpliedType(constraintType); - switch (constraintType.tag) { + return isInherentlyImmutableType(tupRestType) || + isSelectivelyImmutableType(tupRestType, unresolvedTypes, forceCheck, packageID); case TypeTags.RECORD: - Map fieldList = ((BRecordType) constraintType).getFields(); - return fieldList.get(fieldName); - case TypeTags.UNION: - BUnionType unionType = (BUnionType) constraintType; - Set memTypes = unionType.getMemberTypes(); - List fields = memTypes.stream().map(type -> getTableConstraintField(type, fieldName)) - .filter(Objects::nonNull).toList(); - - if (fields.size() != memTypes.size()) { - return null; + BRecordType recordType = (BRecordType) type; + LinkedHashMap recordFields = recordType.fields; + if (recordFields.isEmpty() && recordType.mutableType != null) { + recordFields = recordType.mutableType.fields; } - - if (fields.stream().allMatch(field -> isAssignable(field.type, fields.get(0).type) && - isAssignable(fields.get(0).type, field.type))) { - return fields.get(0); + for (BField field : recordFields.values()) { + BType fieldType = field.type; + if (!Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL) && + !isInherentlyImmutableType(fieldType) && + !isSelectivelyImmutableType(fieldType, unresolvedTypes, forceCheck, packageID)) { + return false; + } } - } - - return null; - } - - private boolean isAssignableMapType(BMapType sourceMapType, BRecordType targetRecType) { - if (targetRecType.sealed) { - return false; - } - - for (BField field : targetRecType.fields.values()) { - if (!Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL)) { - return false; - } - - if (hasIncompatibleReadOnlyFlags(field.symbol.flags, sourceMapType.flags)) { - return false; - } - - if (!isAssignable(sourceMapType.constraint, field.type)) { - return false; - } - } - - return isAssignable(sourceMapType.constraint, targetRecType.restFieldType); - } - - private boolean hasIncompatibleReadOnlyFlags(long targetFlags, long sourceFlags) { - return Symbols.isFlagOn(targetFlags, Flags.READONLY) && !Symbols.isFlagOn(sourceFlags, Flags.READONLY); - } - - private boolean isErrorTypeAssignable(BErrorType source, BErrorType target, Set unresolvedTypes) { - if (target == symTable.errorType) { - return true; - } - TypePair pair = new TypePair(source, target); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - return isAssignable(source.detailType, target.detailType, unresolvedTypes) - && target.typeIdSet.isAssignableFrom(source.typeIdSet); - } - - private boolean isXMLTypeAssignable(BType sourceT, BType targetT, Set unresolvedTypes) { - BType sourceType = getImpliedType(sourceT); - BType targetType = getImpliedType(targetT); - int sourceTag = sourceType.tag; - int targetTag = targetType.tag; - - if (targetTag == TypeTags.XML) { - BXMLType target = (BXMLType) targetType; - if (target.constraint != null) { - if (TypeTags.isXMLNonSequenceType(sourceTag)) { - return isAssignable(sourceType, target.constraint, unresolvedTypes); + return true; + case TypeTags.MAP: + BMapType mapType = (BMapType) type; + BType constraintType = mapType.constraint; + if (constraintType == symTable.semanticError && mapType.mutableType != null) { + constraintType = mapType.mutableType.constraint; } - BXMLType source = (BXMLType) sourceType; - if (getImpliedType(source.constraint).tag == TypeTags.NEVER) { - if (sourceTag == targetTag) { - return true; + return isInherentlyImmutableType(constraintType) || + isSelectivelyImmutableType(constraintType, unresolvedTypes, forceCheck, packageID); + case TypeTags.OBJECT: + BObjectType objectType = (BObjectType) type; + LinkedHashMap objectFields = objectType.fields; + if (objectFields.isEmpty() && objectType.mutableType != null) { + objectFields = objectType.mutableType.fields; + } + for (BField field : objectFields.values()) { + BType fieldType = field.type; + if (!isInherentlyImmutableType(fieldType) && + !isSelectivelyImmutableType(fieldType, unresolvedTypes, forceCheck, packageID)) { + return false; } - return isAssignable(source, target.constraint, unresolvedTypes); } - return isAssignable(source.constraint, target, unresolvedTypes); - } - return true; - } - if (sourceTag == TypeTags.XML) { - BXMLType source = (BXMLType) sourceType; - if (targetTag == TypeTags.XML_TEXT) { - if (source.constraint != null) { - int constraintTag = getImpliedType(source.constraint).tag; - if (constraintTag == TypeTags.NEVER || constraintTag == TypeTags.XML_TEXT) { - return true; - } else { - return isAssignable(source.constraint, targetType, unresolvedTypes); + return true; + case TypeTags.TABLE: + BTableType tableType = (BTableType) type; + BType tableConstraintType = tableType.constraint; + if (tableConstraintType == symTable.semanticError && tableType.mutableType != null) { + tableConstraintType = tableType.mutableType.constraint; + } + return isInherentlyImmutableType(tableConstraintType) || + isSelectivelyImmutableType(tableConstraintType, unresolvedTypes, forceCheck, packageID); + case TypeTags.UNION: + boolean readonlyIntersectionExists = false; + BUnionType unionType = (BUnionType) type; + LinkedHashSet memberTypes = unionType.getMemberTypes(); + for (BType memberType : memberTypes) { + if (isInherentlyImmutableType(memberType) || + isSelectivelyImmutableType(memberType, unresolvedTypes, forceCheck, packageID)) { + readonlyIntersectionExists = true; } } - return false; - } + return readonlyIntersectionExists; } - return sourceTag == targetTag; + return false; } - private boolean isTupleTypeAssignable(BType source, BType target, Set unresolvedTypes) { - TypePair pair = new TypePair(source, target); - if (unresolvedTypes.contains(pair)) { - return true; - } - - source = getImpliedType(source); - if (source.tag == TypeTags.TUPLE && ((BTupleType) source).isCyclic) { - // add cyclic source to target pair to avoid recursive calls - unresolvedTypes.add(pair); - } + public static boolean containsTypeParams(BInvokableType type) { + boolean hasParameterizedTypes = type.paramTypes.stream() + .anyMatch(t -> { + t = getImpliedType(t); + if (t.tag == TypeTags.FUNCTION_POINTER) { + return containsTypeParams((BInvokableType) t); + } + return TypeParamAnalyzer.isTypeParam(t); + }); - target = getImpliedType(target); - if (target.tag == TypeTags.JSON && source.tag == TypeTags.TUPLE) { - BTupleType rhsTupleType = (BTupleType) source; - for (BType tupleType : rhsTupleType.getTupleTypes()) { - if (!isAssignable(tupleType, target, unresolvedTypes)) { - return false; - } - } - if (rhsTupleType.restType != null) { - return isAssignable(rhsTupleType.restType, target, unresolvedTypes); - } + if (hasParameterizedTypes) { return true; } - if (source.tag != TypeTags.TUPLE || target.tag != TypeTags.TUPLE) { - return false; - } - - BTupleType lhsTupleType = (BTupleType) target; - BTupleType rhsTupleType = (BTupleType) source; - - if (lhsTupleType.restType == null && rhsTupleType.restType != null) { - return false; + BType retType = getImpliedType(type.retType); + if (retType.tag == TypeTags.FUNCTION_POINTER) { + return containsTypeParams((BInvokableType) retType); } - if (lhsTupleType.restType == null && - lhsTupleType.getMembers().size() != rhsTupleType.getMembers().size()) { - return false; - } + return TypeParamAnalyzer.isTypeParam(type.retType); + } - if (lhsTupleType.restType != null && rhsTupleType.restType != null) { - if (!isAssignable(rhsTupleType.restType, lhsTupleType.restType, unresolvedTypes)) { - return false; - } - } - List lhsTupleMemberTypes = lhsTupleType.getTupleTypes(); - List rhsTupleMemberTypes = rhsTupleType.getTupleTypes(); - - if (lhsTupleMemberTypes.size() > rhsTupleMemberTypes.size()) { - return false; - } - - for (int i = 0; i < rhsTupleMemberTypes.size(); i++) { - BType lhsType = (lhsTupleMemberTypes.size() > i) - ? lhsTupleMemberTypes.get(i) : lhsTupleType.restType; - if (!isAssignable(rhsTupleMemberTypes.get(i), lhsType, unresolvedTypes)) { - return false; - } - } - return true; - } - - private boolean checkAllTupleMembersBelongNoType(List tupleTypes) { - boolean isNoType = false; - for (BType type : tupleTypes) { - type = getImpliedType(type); - switch (type.tag) { - case TypeTags.NONE: - isNoType = true; - break; - case TypeTags.TUPLE: - isNoType = checkAllTupleMembersBelongNoType(((BTupleType) type).getTupleTypes()); - if (!isNoType) { - return false; - } - break; - default: - return false; - } - } - return isNoType; - } - - private boolean isTupleTypeAssignableToArrayType(BTupleType source, BArrayType target, - Set unresolvedTypes) { - if (target.state != BArrayState.OPEN - && (source.restType != null || source.getMembers().size() != target.size)) { - return false; - } - - List sourceTypes = new ArrayList<>(source.getTupleTypes()); - if (source.restType != null) { - BType type = source.restType; - sourceTypes.add(type); - } - return sourceTypes.stream() - .allMatch(tupleElemType -> isAssignable(tupleElemType, target.eType, unresolvedTypes)); - } - - private boolean isArrayTypeAssignableToTupleType(BArrayType source, BTupleType target, - Set unresolvedTypes) { - BType restType = target.restType; - List tupleTypes = target.getTupleTypes(); - if (source.state == BArrayState.OPEN) { - if (restType == null || !tupleTypes.isEmpty()) { - // [int, int] = int[] || [int, int...] = int[] - return false; - } - - return isAssignable(source.eType, restType, unresolvedTypes); - } - - int targetTupleMemberSize = tupleTypes.size(); - int sourceArraySize = source.size; - - if (targetTupleMemberSize > sourceArraySize) { - // [int, int, int...] = int[1] - return false; - } - - if (restType == null && targetTupleMemberSize < sourceArraySize) { - // [int, int] = int[3] - return false; - } - - BType sourceElementType = source.eType; - for (BType memType : tupleTypes) { - if (!isAssignable(sourceElementType, memType, unresolvedTypes)) { - return false; - } - } - - if (restType == null) { - return true; - } - - return sourceArraySize == targetTupleMemberSize || isAssignable(sourceElementType, restType, unresolvedTypes); - } - - private boolean isArrayTypesAssignable(BArrayType source, BType target, Set unresolvedTypes) { - BType sourceElementType = source.getElementType(); - target = getImpliedType(target); - if (target.tag == TypeTags.ARRAY) { - BArrayType targetArrayType = (BArrayType) target; - BType targetElementType = targetArrayType.getElementType(); - if (targetArrayType.state == BArrayState.OPEN) { - return isAssignable(sourceElementType, targetElementType, unresolvedTypes); - } - - if (targetArrayType.size != source.size) { - return false; - } - - return isAssignable(sourceElementType, targetElementType, unresolvedTypes); - } else if (target.tag == TypeTags.JSON) { - return isAssignable(sourceElementType, target, unresolvedTypes); - } else if (target.tag == TypeTags.ANYDATA) { - return isAssignable(sourceElementType, target, unresolvedTypes); - } - return false; - } - - private boolean isFunctionTypeAssignable(BInvokableType source, BInvokableType target, - Set unresolvedTypes) { - if (hasIncompatibleIsolatedFlags(source, target) || hasIncompatibleTransactionalFlags(source, target)) { - return false; - } - - if (Symbols.isFlagOn(target.flags, Flags.ANY_FUNCTION)) { - return true; - } - if (Symbols.isFlagOn(source.flags, Flags.ANY_FUNCTION) && !Symbols.isFlagOn(target.flags, Flags.ANY_FUNCTION)) { - return false; - } - - // For invokable types with typeParam parameters, we have to check whether the source param types are - // covariant with the target param types. - if (containsTypeParams(target)) { - // TODO: 7/4/19 See if the below code can be generalized to avoid code duplication - if (source.paramTypes.size() != target.paramTypes.size()) { - return false; - } - - for (int i = 0; i < source.paramTypes.size(); i++) { - BType sourceParam = source.paramTypes.get(i); - BType targetParam = target.paramTypes.get(i); - boolean isTypeParam = TypeParamAnalyzer.isTypeParam(targetParam); - - if (isTypeParam) { - if (!isAssignable(sourceParam, targetParam)) { - return false; - } - } else { - if (!isAssignable(targetParam, sourceParam)) { - return false; - } - } - } - - if (source.retType == null && target.retType == null) { - return true; - } else if (source.retType == null || target.retType == null) { - return false; - } - - // Source return type should be covariant with target return type - return isAssignable(source.retType, target.retType, unresolvedTypes); - } - - // Source param types should be contravariant with target param types. Hence s and t switched when checking - // assignability. - return checkFunctionTypeEquality(source, target, unresolvedTypes, (s, t, ut) -> isAssignable(t, s, ut)); - } - - public boolean isInherentlyImmutableType(BType type) { - type = getImpliedType(type); - if (isValueType(type)) { - return true; - } - - return switch (type.tag) { - case TypeTags.XML_TEXT, - TypeTags.FINITE, // Assuming a finite type will only have members from simple basic types. - TypeTags.READONLY, - TypeTags.NIL, - TypeTags.NEVER, - TypeTags.ERROR, - TypeTags.INVOKABLE, - TypeTags.TYPEDESC, - TypeTags.HANDLE, - TypeTags.REGEXP -> true; - case TypeTags.XML -> getImpliedType(((BXMLType) type).constraint).tag == TypeTags.NEVER; - default -> false; - }; - } - - /** - * Retrieve the referred type if a given type is a type reference type or - * retrieve the effective type if the given type is an intersection type. - * - * @param type type to retrieve the implied type - * @return the implied type if provided with a type reference type or an intersection type, - * else returns the original type - */ - public static BType getImpliedType(BType type) { - type = getReferredType(type); - if (type != null && type.tag == TypeTags.INTERSECTION) { - return getImpliedType(((BIntersectionType) type).effectiveType); - } - - return type; - } - - public static BType getReferredType(BType type) { - if (type != null && type.tag == TypeTags.TYPEREFDESC) { - return getReferredType(((BTypeReferenceType) type).referredType); - } - - return type; - } - - public BLangExpression addConversionExprIfRequired(BLangExpression expr, BType lhsType) { - if (lhsType.tag == TypeTags.NONE) { - return expr; - } - - BType rhsType = expr.getBType(); - - if (lhsType.tag == TypeTags.TYPEREFDESC && rhsType.tag != TypeTags.TYPEREFDESC) { - return addConversionExprIfRequired(expr, Types.getReferredType(lhsType)); - } - - if (isSameType(rhsType, lhsType)) { - return expr; - } - - setImplicitCastExpr(expr, rhsType, lhsType); - if (expr.impConversionExpr != null) { - BLangExpression impConversionExpr = expr.impConversionExpr; - expr.impConversionExpr = null; - return impConversionExpr; - } - - if (lhsType.tag == TypeTags.JSON && rhsType.tag == TypeTags.NIL) { - return expr; - } - - if (lhsType.tag == TypeTags.NIL && rhsType.isNullable()) { - return expr; - } - - if (lhsType.tag == TypeTags.ARRAY && rhsType.tag == TypeTags.TUPLE) { - return expr; - } - - // Create a type cast expression - BLangTypeConversionExpr conversionExpr = (BLangTypeConversionExpr) - TreeBuilder.createTypeConversionNode(); - conversionExpr.expr = expr; - conversionExpr.targetType = lhsType; - conversionExpr.setBType(lhsType); - conversionExpr.pos = expr.pos; - conversionExpr.checkTypes = false; - conversionExpr.internal = true; - return conversionExpr; - } - - boolean isSelectivelyImmutableType(BType type, PackageID packageID) { - return isSelectivelyImmutableType(type, new HashSet<>(), false, packageID); - } - - boolean isSelectivelyImmutableType(BType type, boolean forceCheck, PackageID packageID) { - return isSelectivelyImmutableType(type, new HashSet<>(), forceCheck, packageID); - } - - public boolean isSelectivelyImmutableType(BType type, Set unresolvedTypes, PackageID packageID) { - return isSelectivelyImmutableType(type, unresolvedTypes, false, packageID); - } - - private boolean isSelectivelyImmutableType(BType type, Set unresolvedTypes, boolean forceCheck, - PackageID packageID) { - return isSelectivelyImmutableType(type, false, unresolvedTypes, forceCheck, packageID); - } - - private boolean isSelectivelyImmutableType(BType input, boolean disallowReadOnlyObjects, Set unresolvedTypes, - boolean forceCheck, PackageID packageID) { - BType type = getImpliedType(input); - - if (isInherentlyImmutableType(type) || !(type instanceof SelectivelyImmutableReferenceType)) { - // Always immutable. - return false; - } - - if (!unresolvedTypes.add(type)) { - return true; - } - - if (!forceCheck && - getImmutableType(symTable, packageID, (SelectivelyImmutableReferenceType) type).isPresent()) { - return true; - } - - switch (type.tag) { - case TypeTags.ANY: - case TypeTags.ANYDATA: - case TypeTags.JSON: - case TypeTags.XML: - case TypeTags.XML_COMMENT: - case TypeTags.XML_ELEMENT: - case TypeTags.XML_PI: - case TypeTags.BYTE_ARRAY: - return true; - case TypeTags.ARRAY: - BArrayType arrayType = (BArrayType) type; - BType elementType = arrayType.eType; - if (elementType == symTable.semanticError && arrayType.mutableType != null) { - elementType = arrayType.mutableType.eType; - } - return isInherentlyImmutableType(elementType) || - isSelectivelyImmutableType(elementType, unresolvedTypes, forceCheck, packageID); - case TypeTags.TUPLE: - BTupleType tupleType = (BTupleType) type; - List tupleMemberTypes = tupleType.getTupleTypes(); - if (tupleMemberTypes.isEmpty() && tupleType.mutableType != null) { - tupleMemberTypes = tupleType.mutableType.getTupleTypes(); - } - for (BType memberType : tupleMemberTypes) { - if (!isInherentlyImmutableType(memberType) && - !isSelectivelyImmutableType(memberType, unresolvedTypes, forceCheck, packageID)) { - return false; - } - } - - BType tupRestType = tupleType.restType; - if (tupRestType == null) { - return true; - } - - return isInherentlyImmutableType(tupRestType) || - isSelectivelyImmutableType(tupRestType, unresolvedTypes, forceCheck, packageID); - case TypeTags.RECORD: - BRecordType recordType = (BRecordType) type; - LinkedHashMap recordFields = recordType.fields; - if (recordFields.isEmpty() && recordType.mutableType != null) { - recordFields = recordType.mutableType.fields; - } - for (BField field : recordFields.values()) { - BType fieldType = field.type; - if (!Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL) && - !isInherentlyImmutableType(fieldType) && - !isSelectivelyImmutableType(fieldType, unresolvedTypes, forceCheck, packageID)) { - return false; - } - } - return true; - case TypeTags.MAP: - BMapType mapType = (BMapType) type; - BType constraintType = mapType.constraint; - if (constraintType == symTable.semanticError && mapType.mutableType != null) { - constraintType = mapType.mutableType.constraint; - } - return isInherentlyImmutableType(constraintType) || - isSelectivelyImmutableType(constraintType, unresolvedTypes, forceCheck, packageID); - case TypeTags.OBJECT: - BObjectType objectType = (BObjectType) type; - LinkedHashMap objectFields = objectType.fields; - if (objectFields.isEmpty() && objectType.mutableType != null) { - objectFields = objectType.mutableType.fields; - } - for (BField field : objectFields.values()) { - BType fieldType = field.type; - if (!isInherentlyImmutableType(fieldType) && - !isSelectivelyImmutableType(fieldType, unresolvedTypes, forceCheck, packageID)) { - return false; - } - } - return true; - case TypeTags.TABLE: - BTableType tableType = (BTableType) type; - BType tableConstraintType = tableType.constraint; - if (tableConstraintType == symTable.semanticError && tableType.mutableType != null) { - tableConstraintType = tableType.mutableType.constraint; - } - return isInherentlyImmutableType(tableConstraintType) || - isSelectivelyImmutableType(tableConstraintType, unresolvedTypes, forceCheck, packageID); - case TypeTags.UNION: - boolean readonlyIntersectionExists = false; - BUnionType unionType = (BUnionType) type; - LinkedHashSet memberTypes = unionType.getMemberTypes(); - for (BType memberType : memberTypes) { - if (isInherentlyImmutableType(memberType) || - isSelectivelyImmutableType(memberType, unresolvedTypes, forceCheck, packageID)) { - readonlyIntersectionExists = true; - } - } - return readonlyIntersectionExists; - } - return false; - } - - private boolean containsTypeParams(BInvokableType type) { - boolean hasParameterizedTypes = type.paramTypes.stream() - .anyMatch(t -> { - t = getImpliedType(t); - if (t.tag == TypeTags.FUNCTION_POINTER) { - return containsTypeParams((BInvokableType) t); - } - return TypeParamAnalyzer.isTypeParam(t); - }); - - if (hasParameterizedTypes) { - return true; - } - - BType retType = getImpliedType(type.retType); - if (retType.tag == TypeTags.FUNCTION_POINTER) { - return containsTypeParams((BInvokableType) retType); - } - - return TypeParamAnalyzer.isTypeParam(type.retType); - } - - private boolean checkFunctionTypeEquality(BInvokableType source, BInvokableType target, - Set unresolvedTypes, TypeEqualityPredicate equality) { - if (hasIncompatibleIsolatedFlags(source, target) || hasIncompatibleTransactionalFlags(source, target)) { - return false; - } - - if (Symbols.isFlagOn(target.flags, Flags.ANY_FUNCTION) && Symbols.isFlagOn(source.flags, Flags.ANY_FUNCTION)) { - return true; - } - - if (Symbols.isFlagOn(target.flags, Flags.ANY_FUNCTION) || Symbols.isFlagOn(source.flags, Flags.ANY_FUNCTION)) { - return false; - } - - if (source.paramTypes.size() != target.paramTypes.size()) { - return false; - } - - for (int i = 0; i < source.paramTypes.size(); i++) { - if (!equality.test(source.paramTypes.get(i), target.paramTypes.get(i), unresolvedTypes)) { - return false; - } - } - - if ((source.restType != null && target.restType == null) || - target.restType != null && source.restType == null) { - return false; - } else if (source.restType != null && !equality.test(source.restType, target.restType, unresolvedTypes)) { - return false; - } - - if (source.retType == null && target.retType == null) { - return true; - } else if (source.retType == null || target.retType == null) { - return false; - } - - // Source return type should be covariant with target return type - return isAssignable(source.retType, target.retType, unresolvedTypes); - } - - private boolean hasIncompatibleIsolatedFlags(BInvokableType source, BInvokableType target) { - return Symbols.isFlagOn(target.flags, Flags.ISOLATED) && !Symbols.isFlagOn(source.flags, Flags.ISOLATED); - } - - private boolean hasIncompatibleTransactionalFlags(BInvokableType source, BInvokableType target) { - return Symbols.isFlagOn(source.flags, Flags.TRANSACTIONAL) && - !Symbols.isFlagOn(target.flags, Flags.TRANSACTIONAL); - } - - public boolean isSameArrayType(BType source, BType target, Set unresolvedTypes) { - target = getImpliedType(target); - source = getImpliedType(source); - if (target.tag != TypeTags.ARRAY || source.tag != TypeTags.ARRAY) { - return false; - } - - BArrayType lhsArrayType = (BArrayType) target; - BArrayType rhsArrayType = (BArrayType) source; - boolean hasSameTypeElements = isSameType(lhsArrayType.eType, rhsArrayType.eType, unresolvedTypes); - if (lhsArrayType.state == BArrayState.OPEN) { - return (rhsArrayType.state == BArrayState.OPEN) && hasSameTypeElements; - } - - return checkSealedArraySizeEquality(rhsArrayType, lhsArrayType) && hasSameTypeElements; - } - - public boolean checkSealedArraySizeEquality(BArrayType rhsArrayType, BArrayType lhsArrayType) { - return lhsArrayType.size == rhsArrayType.size; - } - - public boolean checkStructEquivalency(BType rhsType, BType lhsType) { - return checkStructEquivalency(rhsType, lhsType, new HashSet<>()); - } - - private boolean checkStructEquivalency(BType rhsType, BType lhsType, Set unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(rhsType, lhsType); - if (unresolvedTypes.contains(pair)) { - return true; - } - unresolvedTypes.add(pair); - - rhsType = getImpliedType(rhsType); - lhsType = getImpliedType(lhsType); - if (rhsType.tag == TypeTags.OBJECT && lhsType.tag == TypeTags.OBJECT) { - return checkObjectEquivalency((BObjectType) rhsType, (BObjectType) lhsType, unresolvedTypes); - } - - if (rhsType.tag == TypeTags.RECORD && lhsType.tag == TypeTags.RECORD) { - return checkRecordEquivalency((BRecordType) rhsType, (BRecordType) lhsType, unresolvedTypes); - } - - return false; - } - - public boolean checkObjectEquivalency(BObjectType rhsType, BObjectType lhsType, Set unresolvedTypes) { - if (Symbols.isFlagOn(lhsType.flags, Flags.ISOLATED) && !Symbols.isFlagOn(rhsType.flags, Flags.ISOLATED)) { - return false; - } - - BObjectTypeSymbol lhsStructSymbol = (BObjectTypeSymbol) lhsType.tsymbol; - BObjectTypeSymbol rhsStructSymbol = (BObjectTypeSymbol) rhsType.tsymbol; - List lhsFuncs = lhsStructSymbol.attachedFuncs; - List rhsFuncs = ((BObjectTypeSymbol) rhsType.tsymbol).attachedFuncs; - int lhsAttachedFuncCount = getObjectFuncCount(lhsStructSymbol); - int rhsAttachedFuncCount = getObjectFuncCount(rhsStructSymbol); - - // If LHS is a service obj, then RHS must be a service object in order to assignable - boolean isLhsAService = Symbols.isService(lhsStructSymbol); - if (isLhsAService && !Symbols.isService(rhsStructSymbol)) { - return false; - } - - // RHS type should have at least all the fields as well attached functions of LHS type. - if (lhsType.fields.size() > rhsType.fields.size() || lhsAttachedFuncCount > rhsAttachedFuncCount) { - return false; - } - - // The LHS type cannot have any private members. - for (BField bField : lhsType.fields.values()) { - if (Symbols.isPrivate(bField.symbol)) { - return false; - } - } - - for (BAttachedFunction func : lhsFuncs) { - if (Symbols.isPrivate(func.symbol)) { - return false; - } - } - - for (BField lhsField : lhsType.fields.values()) { - BField rhsField = rhsType.fields.get(lhsField.name.value); - if (rhsField == null || - !isInSameVisibilityRegion(lhsField.symbol, rhsField.symbol) || - !isAssignable(rhsField.type, lhsField.type, unresolvedTypes)) { - return false; - } - } - - for (BAttachedFunction lhsFunc : lhsFuncs) { - if (lhsFunc == lhsStructSymbol.initializerFunc) { - continue; - } - - Optional rhsFunction = getMatchingInvokableType(rhsFuncs, lhsFunc, unresolvedTypes); - if (rhsFunction.isEmpty()) { - return false; - } - - BAttachedFunction rhsFunc = rhsFunction.get(); - if (!isInSameVisibilityRegion(lhsFunc.symbol, rhsFunc.symbol)) { - return false; - } - - if (Symbols.isRemote(lhsFunc.symbol) != Symbols.isRemote(rhsFunc.symbol)) { - return false; - } - } - - return lhsType.typeIdSet.isAssignableFrom(rhsType.typeIdSet) || this.ignoreObjectTypeIds; - } - - private int getObjectFuncCount(BObjectTypeSymbol sym) { - int count = sym.attachedFuncs.size(); - // If an explicit initializer is available, it could mean, - // 1) User explicitly defined an initializer - // 2) The object type is coming from an already compiled source, hence the initializer is already set. - // If it's coming from a compiled binary, the attached functions list of the symbol would already contain - // the initializer in it. - if (sym.initializerFunc != null && sym.attachedFuncs.contains(sym.initializerFunc)) { - return count - 1; - } - return count; - } - - public boolean checkRecordEquivalency(BRecordType rhsType, BRecordType lhsType, Set unresolvedTypes) { - // If the LHS record is closed and the RHS record is open and the rest field type of RHS is not a 'never' - // type, the records aren't equivalent - if (lhsType.sealed && !rhsType.sealed && getImpliedType(rhsType.restFieldType).tag != TypeTags.NEVER) { - return false; - } - - // If both are open records, the rest field type of the RHS record should be assignable to the rest field - // type of the LHS type. - if (!rhsType.sealed && !isAssignable(rhsType.restFieldType, lhsType.restFieldType, unresolvedTypes)) { - return false; - } - - return checkFieldEquivalency(lhsType, rhsType, unresolvedTypes); - } - - public void setForeachTypedBindingPatternType(BLangForeach foreachNode) { - BType collectionType = getImpliedType(foreachNode.collection.getBType()); - BType varType; - switch (collectionType.tag) { - case TypeTags.STRING: - varType = symTable.charStringType; - break; - case TypeTags.ARRAY: - BArrayType arrayType = (BArrayType) collectionType; - varType = arrayType.eType; - break; - case TypeTags.TUPLE: - varType = getTupleMemberType((BTupleType) collectionType); - break; - case TypeTags.MAP: - BMapType bMapType = (BMapType) collectionType; - varType = bMapType.constraint; - break; - case TypeTags.RECORD: - BRecordType recordType = (BRecordType) collectionType; - varType = inferRecordFieldType(recordType); - break; - case TypeTags.XML: - BType typedBindingPatternType = getTypedBindingPatternTypeForXmlCollection(collectionType); - if (typedBindingPatternType == null) { - foreachNode.varType = symTable.semanticError; - foreachNode.resultType = symTable.semanticError; - foreachNode.nillableResultType = symTable.semanticError; - return; - } - varType = typedBindingPatternType; - break; - case TypeTags.XML_TEXT: - varType = symTable.xmlTextType; - break; - case TypeTags.TABLE: - BTableType tableType = (BTableType) collectionType; - varType = tableType.constraint; - break; - case TypeTags.STREAM: - BStreamType streamType = (BStreamType) collectionType; - if (streamType.constraint.tag == TypeTags.NONE) { - varType = symTable.anydataType; - break; - } - varType = streamType.constraint; - List completionType = getAllTypes(streamType.completionType, true); - if (completionType.stream().anyMatch(type -> getImpliedType(type).tag != TypeTags.NIL)) { - BType actualType = BUnionType.create(null, varType, streamType.completionType); - dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, - varType, actualType); - } - break; - case TypeTags.OBJECT: - // check for iterable objects - BUnionType nextMethodReturnType = getVarTypeFromIterableObject((BObjectType) collectionType); - if (nextMethodReturnType != null) { - foreachNode.resultType = getRecordType(nextMethodReturnType); - BType valueType = (foreachNode.resultType != null) - ? ((BRecordType) foreachNode.resultType).fields.get("value").type : null; - BType errorType = getErrorType(nextMethodReturnType); - if (errorType != null) { - BType actualType = BUnionType.create(null, valueType, errorType); - dlog.error(foreachNode.collection.pos, - DiagnosticErrorCode.INVALID_ITERABLE_COMPLETION_TYPE_IN_FOREACH_NEXT_FUNCTION, - actualType, errorType); - } - foreachNode.nillableResultType = nextMethodReturnType; - foreachNode.varType = valueType; - return; - } - // fallthrough - case TypeTags.SEMANTIC_ERROR: - foreachNode.varType = symTable.semanticError; - foreachNode.resultType = symTable.semanticError; - foreachNode.nillableResultType = symTable.semanticError; - return; - default: - foreachNode.varType = symTable.semanticError; - foreachNode.resultType = symTable.semanticError; - foreachNode.nillableResultType = symTable.semanticError; - dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.ITERABLE_NOT_SUPPORTED_COLLECTION, - collectionType); - return; - } - - BInvokableSymbol iteratorSymbol = (BInvokableSymbol) symResolver.lookupLangLibMethod(collectionType, - Names.fromString(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC), env); - BObjectType objectType = (BObjectType) getImpliedType(iteratorSymbol.retType); - BUnionType nextMethodReturnType = - (BUnionType) getResultTypeOfNextInvocation(objectType); - foreachNode.varType = varType; - foreachNode.resultType = getRecordType(nextMethodReturnType); - foreachNode.nillableResultType = nextMethodReturnType; - } - - public void setInputClauseTypedBindingPatternType(BLangInputClause bLangInputClause) { - if (bLangInputClause.collection == null) { - //not-possible - return; - } - - BType collectionType = bLangInputClause.collection.getBType(); - bLangInputClause.varType = visitCollectionType(bLangInputClause, collectionType); - if (bLangInputClause.varType.tag == TypeTags.SEMANTIC_ERROR || - getImpliedType(collectionType).tag == OBJECT) { - return; - } - - BInvokableSymbol iteratorSymbol = (BInvokableSymbol) symResolver.lookupLangLibMethod(collectionType, - Names.fromString(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC), env); - BUnionType nextMethodReturnType = - (BUnionType) getResultTypeOfNextInvocation((BObjectType) getImpliedType(iteratorSymbol.retType)); - bLangInputClause.resultType = getRecordType(nextMethodReturnType); - bLangInputClause.nillableResultType = nextMethodReturnType; - } - - private BType getTypedBindingPatternTypeForXmlCollection(BType collectionType) { - BType constraint = getImpliedType(((BXMLType) collectionType).constraint); - while (constraint.tag == TypeTags.XML) { - collectionType = constraint; - constraint = getImpliedType(((BXMLType) collectionType).constraint); - } - - switch (constraint.tag) { - case TypeTags.XML_ELEMENT: - case TypeTags.XML_COMMENT: - case TypeTags.XML_TEXT: - case TypeTags.XML_PI: - case TypeTags.NEVER: - return constraint; - case TypeTags.UNION: - Set collectionTypes = getEffectiveMemberTypes((BUnionType) constraint); - Set builtinXMLConstraintTypes = getEffectiveMemberTypes - ((BUnionType) ((BXMLType) symTable.xmlType).constraint); - return collectionTypes.size() == 4 && builtinXMLConstraintTypes.equals(collectionTypes) ? - collectionType : BUnionType.create(null, (LinkedHashSet) collectionTypes); - default: - return null; - } - } - - private BType visitCollectionType(BLangInputClause bLangInputClause, BType collectionType) { - collectionType = getImpliedType(collectionType); - switch (collectionType.tag) { - case TypeTags.STRING: - return symTable.stringType; - case TypeTags.ARRAY: - BArrayType arrayType = (BArrayType) collectionType; - return arrayType.eType; - case TypeTags.TUPLE: - return getTupleMemberType((BTupleType) collectionType); - case TypeTags.MAP: - BMapType bMapType = (BMapType) collectionType; - return bMapType.constraint; - case TypeTags.RECORD: - BRecordType recordType = (BRecordType) collectionType; - return inferRecordFieldType(recordType); - case TypeTags.XML: - BType bindingPatternType = getTypedBindingPatternTypeForXmlCollection(collectionType); - return bindingPatternType == null ? symTable.semanticError : bindingPatternType; - case TypeTags.XML_TEXT: - return symTable.xmlTextType; - case TypeTags.TABLE: - BTableType tableType = (BTableType) collectionType; - return tableType.constraint; - case TypeTags.STREAM: - BStreamType streamType = (BStreamType) collectionType; - if (streamType.constraint.tag == TypeTags.NONE) { - return symTable.anydataType; - } - return streamType.constraint; - case TypeTags.OBJECT: - // check for iterable objects - if (!isAssignable(collectionType, symTable.iterableType)) { - dlog.error(bLangInputClause.collection.pos, DiagnosticErrorCode.INVALID_ITERABLE_OBJECT_TYPE, - bLangInputClause.collection.getBType(), symTable.iterableType); - bLangInputClause.varType = symTable.semanticError; - bLangInputClause.resultType = symTable.semanticError; - bLangInputClause.nillableResultType = symTable.semanticError; - break; - } - - BUnionType nextMethodReturnType = getVarTypeFromIterableObject((BObjectType) collectionType); - if (nextMethodReturnType != null) { - bLangInputClause.resultType = getRecordType(nextMethodReturnType); - bLangInputClause.nillableResultType = nextMethodReturnType; - bLangInputClause.varType = ((BRecordType) bLangInputClause.resultType).fields.get("value").type; - return bLangInputClause.varType; - } - // fallthrough - case TypeTags.SEMANTIC_ERROR: - bLangInputClause.varType = symTable.semanticError; - bLangInputClause.resultType = symTable.semanticError; - bLangInputClause.nillableResultType = symTable.semanticError; - break; - default: - bLangInputClause.varType = symTable.semanticError; - bLangInputClause.resultType = symTable.semanticError; - bLangInputClause.nillableResultType = symTable.semanticError; - dlog.error(bLangInputClause.collection.pos, DiagnosticErrorCode.ITERABLE_NOT_SUPPORTED_COLLECTION, - collectionType); - } - return symTable.semanticError; - } - - private BType getTupleMemberType(BTupleType tupleType) { - LinkedHashSet tupleTypes = new LinkedHashSet<>(tupleType.getTupleTypes()); - if (tupleType.restType != null) { - tupleTypes.add(tupleType.restType); - } - int tupleTypesSize = tupleTypes.size(); - if (tupleTypesSize == 0) { - return symTable.neverType; - } - return tupleTypesSize == 1 ? - tupleTypes.iterator().next() : BUnionType.create(null, tupleTypes); - } - - public BUnionType getVarTypeFromIterableObject(BObjectType collectionType) { - BObjectTypeSymbol objectTypeSymbol = (BObjectTypeSymbol) collectionType.tsymbol; - for (BAttachedFunction func : objectTypeSymbol.attachedFuncs) { - if (func.funcName.value.equals(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC)) { - return getVarTypeFromIteratorFunc(func); - } - } - - return null; - } - - private BUnionType getVarTypeFromIteratorFunc(BAttachedFunction candidateIteratorFunc) { - if (!candidateIteratorFunc.type.paramTypes.isEmpty()) { - return null; - } - - BType returnType = candidateIteratorFunc.type.retType; - // abstract object {public function next() returns record {|int value;|}?;} - return getVarTypeFromIteratorFuncReturnType(returnType); - } - - public BUnionType getVarTypeFromIteratorFuncReturnType(BType type) { - BObjectTypeSymbol objectTypeSymbol; - BType returnType = getImpliedType(type); - if (returnType.tag != TypeTags.OBJECT) { - return null; - } - - objectTypeSymbol = (BObjectTypeSymbol) returnType.tsymbol; - for (BAttachedFunction func : objectTypeSymbol.attachedFuncs) { - if (func.funcName.value.equals(BLangCompilerConstants.NEXT_FUNC)) { - return getVarTypeFromNextFunc(func); - } - } - - return null; - } - - private BUnionType getVarTypeFromNextFunc(BAttachedFunction nextFunc) { - BType returnType; - if (!nextFunc.type.paramTypes.isEmpty()) { - return null; - } - - returnType = nextFunc.type.retType; - // Check if the next function return type has the union type, - // record {|int value;|}|error|(); - if (checkNextFuncReturnType(returnType)) { - return (BUnionType) returnType; - } - - return null; - } - - private boolean checkNextFuncReturnType(BType returnType) { - if (getImpliedType(returnType).tag != TypeTags.UNION) { - return false; - } - - List types = getAllTypes(returnType, true); - boolean containsCompletionType = types.removeIf(type -> type.tag == TypeTags.NIL); - containsCompletionType = types.removeIf(type -> type.tag == TypeTags.ERROR) || containsCompletionType; - if (!containsCompletionType) { - return false; - } - - if (types.size() != 1) { - //TODO: print error - return false; - } - - if (types.get(0).tag != TypeTags.RECORD) { - return false; - } - - BRecordType recordType = (BRecordType) types.get(0); - // Check if the union type has the record type, - // record {|int value;|}; - return checkRecordTypeInNextFuncReturnType(recordType); - } - - private boolean checkRecordTypeInNextFuncReturnType(BRecordType recordType) { - if (!recordType.sealed) { - return false; - } - - if (recordType.fields.size() != 1) { - return false; - } - - return recordType.fields.containsKey(BLangCompilerConstants.VALUE_FIELD); - } - - private BRecordType getRecordType(BUnionType type) { - for (BType member : type.getMemberTypes()) { - BType referredRecordType = getImpliedType(member); - if (referredRecordType.tag == TypeTags.RECORD) { - return (BRecordType) referredRecordType; - } - } - return null; - } - - public BErrorType getErrorType(BUnionType type) { - for (BType member : type.getMemberTypes()) { - member = getImpliedType(member); - - if (member.tag == TypeTags.ERROR) { - return (BErrorType) member; - } else if (member.tag == TypeTags.UNION) { - BErrorType e = getErrorType((BUnionType) member); - if (e != null) { - return e; - } - } - } - return null; - } - - public BType getResultTypeOfNextInvocation(BObjectType iteratorType) { - BAttachedFunction nextFunc = getAttachedFuncFromObject(iteratorType, BLangCompilerConstants.NEXT_FUNC); - return Objects.requireNonNull(nextFunc).type.retType; - } - - public BAttachedFunction getAttachedFuncFromObject(BObjectType objectType, String funcName) { - BObjectTypeSymbol iteratorSymbol = (BObjectTypeSymbol) objectType.tsymbol; - for (BAttachedFunction bAttachedFunction : iteratorSymbol.attachedFuncs) { - if (funcName.equals(bAttachedFunction.funcName.value)) { - return bAttachedFunction; - } - } - return null; - } - - public BType inferRecordFieldType(BRecordType recordType) { - Map fields = recordType.fields; - BUnionType unionType = BUnionType.create(null); - - if (!recordType.sealed) { - unionType.add(recordType.restFieldType); - } else if (fields.isEmpty()) { - unionType.add(symTable.neverType); - } - - for (BField field : fields.values()) { - if (isAssignable(field.type, unionType)) { - continue; - } - - if (isAssignable(unionType, field.type)) { - unionType = BUnionType.create(null); - } - - unionType.add(field.type); - } - - if (unionType.getMemberTypes().size() > 1) { - unionType.tsymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, Flags.asMask(EnumSet.of(Flag.PUBLIC)), - Names.EMPTY, recordType.tsymbol.pkgID, null, - recordType.tsymbol.owner, symTable.builtinPos, VIRTUAL); - return unionType; - } - - return unionType.getMemberTypes().iterator().next(); - } - - - public BType getTypeWithEffectiveIntersectionTypes(BType bType) { - // TODO Can remove this method since this unwraps the referred type and intersection type. #40958 - BType type = getReferredType(bType); - BType effectiveType = null; - if (type.tag == TypeTags.INTERSECTION) { - effectiveType = ((BIntersectionType) type).effectiveType; - type = effectiveType; - } - - if (type.tag != TypeTags.UNION) { - return Objects.requireNonNullElse(effectiveType, bType); - } - - LinkedHashSet members = new LinkedHashSet<>(); - boolean hasDifferentMember = false; - - for (BType memberType : ((BUnionType) type).getMemberTypes()) { - effectiveType = getTypeWithEffectiveIntersectionTypes(memberType); - effectiveType = getImpliedType(effectiveType); - if (effectiveType != memberType) { - hasDifferentMember = true; - } - members.add(effectiveType); - } - - if (hasDifferentMember) { - return BUnionType.create(null, members); - } - return bType; - } - - /** - * Enum to represent type test result. - * - * @since 1.2.0 - */ - enum TypeTestResult { - NOT_FOUND, - TRUE, - FALSE - } - - TypeTestResult isBuiltInTypeWidenPossible(BType actualType, BType targetType) { - int targetTag = getImpliedType(targetType).tag; - int actualTag = getImpliedType(actualType).tag; - - if (actualTag < TypeTags.JSON && targetTag < TypeTags.JSON) { - // Fail Fast for value types. - switch (actualTag) { - case TypeTags.INT: - case TypeTags.BYTE: - case TypeTags.FLOAT: - case TypeTags.DECIMAL: - if (targetTag == TypeTags.BOOLEAN || targetTag == TypeTags.STRING) { - return TypeTestResult.FALSE; - } - break; - case TypeTags.BOOLEAN: - if (targetTag == TypeTags.INT || targetTag == TypeTags.BYTE || targetTag == TypeTags.FLOAT - || targetTag == TypeTags.DECIMAL || targetTag == TypeTags.STRING) { - return TypeTestResult.FALSE; - } - break; - case TypeTags.STRING: - if (targetTag == TypeTags.INT || targetTag == TypeTags.BYTE || targetTag == TypeTags.FLOAT - || targetTag == TypeTags.DECIMAL || targetTag == TypeTags.BOOLEAN) { - return TypeTestResult.FALSE; - } - break; - } - } - switch (actualTag) { - case TypeTags.INT: - case TypeTags.BYTE: - case TypeTags.FLOAT: - case TypeTags.DECIMAL: - case TypeTags.BOOLEAN: + public void setForeachTypedBindingPatternType(BLangForeach foreachNode) { + BType collectionType = getImpliedType(foreachNode.collection.getBType()); + BType varType; + switch (collectionType.tag) { case TypeTags.STRING: - case TypeTags.SIGNED32_INT: - case TypeTags.SIGNED16_INT: - case TypeTags.SIGNED8_INT: - case TypeTags.UNSIGNED32_INT: - case TypeTags.UNSIGNED16_INT: - case TypeTags.UNSIGNED8_INT: - case TypeTags.CHAR_STRING: - if (targetTag == TypeTags.JSON || targetTag == TypeTags.ANYDATA || targetTag == TypeTags.ANY || - targetTag == TypeTags.READONLY) { - return TypeTestResult.TRUE; - } + varType = symTable.charStringType; break; - case TypeTags.ANYDATA: - case TypeTags.TYPEDESC: - if (targetTag == TypeTags.ANY) { - return TypeTestResult.TRUE; - } + case TypeTags.ARRAY: + BArrayType arrayType = (BArrayType) collectionType; + varType = arrayType.eType; break; - default: - } - - if (TypeTags.isIntegerTypeTag(targetTag) && actualTag == targetTag) { - return TypeTestResult.FALSE; // No widening. - } - - // Validate for Integers subtypes. - if ((TypeTags.isIntegerTypeTag(actualTag) || actualTag == TypeTags.BYTE) - && (TypeTags.isIntegerTypeTag(targetTag) || targetTag == TypeTags.BYTE)) { - return checkBuiltInIntSubtypeWidenPossible(actualType, targetType); - } - - if (actualTag == TypeTags.CHAR_STRING && TypeTags.STRING == targetTag) { - return TypeTestResult.TRUE; - } - return TypeTestResult.NOT_FOUND; - } - - private TypeTestResult checkBuiltInIntSubtypeWidenPossible(BType actualType, BType targetType) { - int actualTag = getImpliedType(actualType).tag; - targetType = getImpliedType(targetType); - switch (targetType.tag) { - case TypeTags.INT: - if (actualTag == TypeTags.BYTE || TypeTags.isIntegerTypeTag(actualTag)) { - return TypeTestResult.TRUE; - } + case TypeTags.TUPLE: + varType = getTupleMemberType((BTupleType) collectionType); break; - case TypeTags.SIGNED32_INT: - if (actualTag == TypeTags.SIGNED16_INT || actualTag == TypeTags.SIGNED8_INT || - actualTag == TypeTags.UNSIGNED16_INT || actualTag == TypeTags.UNSIGNED8_INT || - actualTag == TypeTags.BYTE) { - return TypeTestResult.TRUE; - } + case TypeTags.MAP: + BMapType bMapType = (BMapType) collectionType; + varType = bMapType.constraint; break; - case TypeTags.SIGNED16_INT: - if (actualTag == TypeTags.SIGNED8_INT || actualTag == TypeTags.UNSIGNED8_INT || - actualTag == TypeTags.BYTE) { - return TypeTestResult.TRUE; - } + case TypeTags.RECORD: + BRecordType recordType = (BRecordType) collectionType; + varType = inferRecordFieldType(recordType); break; - case TypeTags.UNSIGNED32_INT: - if (actualTag == TypeTags.UNSIGNED16_INT || actualTag == TypeTags.UNSIGNED8_INT || - actualTag == TypeTags.BYTE) { - return TypeTestResult.TRUE; + case TypeTags.XML: + BType typedBindingPatternType = getTypedBindingPatternTypeForXmlCollection(collectionType); + if (typedBindingPatternType == null) { + foreachNode.varType = symTable.semanticError; + foreachNode.resultType = symTable.semanticError; + foreachNode.nillableResultType = symTable.semanticError; + return; } + varType = typedBindingPatternType; break; - case TypeTags.UNSIGNED16_INT: - if (actualTag == TypeTags.UNSIGNED8_INT || actualTag == TypeTags.BYTE) { - return TypeTestResult.TRUE; - } + case TypeTags.XML_TEXT: + varType = symTable.xmlTextType; break; - case TypeTags.BYTE: - if (actualTag == TypeTags.UNSIGNED8_INT) { - return TypeTestResult.TRUE; - } + case TypeTags.TABLE: + BTableType tableType = (BTableType) collectionType; + varType = tableType.constraint; break; - case TypeTags.UNSIGNED8_INT: - if (actualTag == TypeTags.BYTE) { - return TypeTestResult.TRUE; - } - } - return TypeTestResult.NOT_FOUND; - } - - public boolean isImplicitlyCastable(BType actual, BType target) { - /* The word Builtin refers for Compiler known types. */ - - BType targetType = getImpliedType(target); - BType actualType = getImpliedType(actual); - BType newTargetType = targetType; - int targetTypeTag = targetType.tag; - if ((targetTypeTag == TypeTags.UNION || targetTypeTag == TypeTags.FINITE) && isValueType(actualType)) { - newTargetType = symTable.anyType; // TODO : Check for correctness. - } - - TypeTestResult result = isBuiltInTypeWidenPossible(actualType, newTargetType); - if (result != TypeTestResult.NOT_FOUND) { - return result == TypeTestResult.TRUE; - } - - if (isValueType(targetType) && - (actualType.tag == TypeTags.FINITE || - (actualType.tag == TypeTags.UNION && ((BUnionType) actualType).getMemberTypes().stream() - .anyMatch(type -> getImpliedType(type).tag == TypeTags.FINITE && - isAssignable(type, targetType))))) { - // for nil, no cast is required - return TypeTags.isIntegerTypeTag(targetTypeTag) || targetType.tag == TypeTags.BYTE || - targetTypeTag == TypeTags.FLOAT || - targetTypeTag == TypeTags.DECIMAL || - TypeTags.isStringTypeTag(targetTypeTag) || - targetTypeTag == TypeTags.BOOLEAN; - - } else if (isValueType(targetType) && actualType.tag == TypeTags.UNION && - ((BUnionType) actualType).getMemberTypes().stream().allMatch(type -> isAssignable(type, targetType))) { - return true; - - } else if (targetTypeTag == TypeTags.ERROR - && (actualType.tag == TypeTags.UNION - && isAllErrorMembers((BUnionType) actualType))) { - return true; - } - return false; - } - - public boolean isTypeCastable(BLangExpression expr, BType source, BType target, SymbolEnv env) { - BType sourceType = getImpliedType(source); - BType targetType = getImpliedType(target); - if (sourceType.tag == TypeTags.SEMANTIC_ERROR || targetType.tag == TypeTags.SEMANTIC_ERROR || - sourceType == targetType) { - return true; - } - - // Disallow casting away error, this forces user to handle the error via type-test, check, or checkpanic - IntersectionContext intersectionContext = IntersectionContext.compilerInternalIntersectionTestContext(); - BType errorIntersection = getTypeIntersection(intersectionContext, sourceType, symTable.errorType, env); - if (errorIntersection != symTable.semanticError && - getTypeIntersection(intersectionContext, symTable.errorType, targetType, env) - == symTable.semanticError) { - return false; - } - - if (isAssignable(sourceType, targetType) || isAssignable(targetType, sourceType)) { - return true; - } - if (isNumericConversionPossible(expr, sourceType, targetType)) { - return true; - } - if (sourceType.tag == TypeTags.ANY && targetType.tag == TypeTags.READONLY) { - return true; - } - - boolean validTypeCast = false; - - // Use instanceof to check for anydata and json. - if (sourceType instanceof BUnionType) { - if (getTypeForUnionTypeMembersAssignableToType((BUnionType) sourceType, targetType, env, - intersectionContext, new LinkedHashSet<>()) - != symTable.semanticError) { - // string|typedesc v1 = "hello world"; - // json|table v2 = > v1; - validTypeCast = true; - } - } - - // Use instanceof to check for anydata and json. - if (targetType instanceof BUnionType) { - if (getTypeForUnionTypeMembersAssignableToType((BUnionType) targetType, sourceType, env, - intersectionContext, new LinkedHashSet<>()) - != symTable.semanticError) { - // string|int v1 = "hello world"; - // string|boolean v2 = v1; - validTypeCast = true; - } - } - - if (sourceType.tag == TypeTags.FINITE) { - if (getTypeForFiniteTypeValuesAssignableToType((BFiniteType) sourceType, targetType) - != symTable.semanticError) { - validTypeCast = true; - } - } - - if (targetType.tag == TypeTags.FINITE) { - if (getTypeForFiniteTypeValuesAssignableToType((BFiniteType) targetType, sourceType) - != symTable.semanticError) { - validTypeCast = true; - } - } - - if (validTypeCast) { - if (isValueType(sourceType)) { - setImplicitCastExpr(expr, sourceType, symTable.anyType); - } - return true; - } - - return false; - } - - boolean isNumericConversionPossible(BLangExpression expr, BType sourceType, - BType targetType) { - - final boolean isSourceNumericType = isBasicNumericType(sourceType); - final boolean isTargetNumericType = isBasicNumericType(targetType); - if (isSourceNumericType && isTargetNumericType) { - // We only reach here for different numeric types. - // 2019R3 Spec defines numeric conversion between each type. - return true; - } - if (targetType.tag == TypeTags.UNION) { - HashSet typeTags = new HashSet<>(); - for (BType bType : ((BUnionType) targetType).getMemberTypes()) { - if (isBasicNumericType(bType)) { - typeTags.add(getImpliedType(bType).tag); - if (typeTags.size() > 1) { - // Multiple Basic numeric types found in the union. - return false; - } + case TypeTags.STREAM: + BStreamType streamType = (BStreamType) collectionType; + if (streamType.constraint.tag == TypeTags.NONE) { + varType = symTable.anydataType; + break; } - } - } - - if (!isTargetNumericType && targetType.tag != TypeTags.UNION) { - return false; - } - - // Target type has at least one numeric type member. - - if (isSourceNumericType) { - // i.e., a conversion from a numeric type to another numeric type in a union. - // int|string u1 = 1.0; - // TODO : Fix me. This doesn't belong here. - setImplicitCastExpr(expr, sourceType, symTable.anyType); - return true; - } - - // TODO : Do we need this? This doesn't belong here. - switch (sourceType.tag) { - case TypeTags.ANY: - case TypeTags.ANYDATA: - case TypeTags.JSON: - // This - return true; - case TypeTags.UNION: - for (BType memType : ((BUnionType) sourceType).getMemberTypes()) { - BType referredType = getImpliedType(memType); - if (isBasicNumericType(referredType) || - (referredType.tag == TypeTags.FINITE && - finiteTypeContainsNumericTypeValues((BFiniteType) referredType))) { - return true; - } + varType = streamType.constraint; + List completionType = getAllTypes(streamType.completionType, true); + if (completionType.stream().anyMatch(type -> getImpliedType(type).tag != TypeTags.NIL)) { + BType actualType = BUnionType.create(typeEnv(), null, varType, streamType.completionType); + dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPES, + varType, actualType); } break; - case TypeTags.FINITE: - if (finiteTypeContainsNumericTypeValues((BFiniteType) sourceType)) { - return true; + case TypeTags.OBJECT: + // check for iterable objects + BUnionType nextMethodReturnType = getVarTypeFromIterableObject((BObjectType) collectionType); + if (nextMethodReturnType != null) { + foreachNode.resultType = getRecordType(nextMethodReturnType); + BType valueType = (foreachNode.resultType != null) + ? ((BRecordType) foreachNode.resultType).fields.get("value").type : null; + BType errorType = getErrorType(nextMethodReturnType); + if (errorType != null) { + BType actualType = BUnionType.create(typeEnv(), null, valueType, errorType); + dlog.error(foreachNode.collection.pos, + DiagnosticErrorCode.INVALID_ITERABLE_COMPLETION_TYPE_IN_FOREACH_NEXT_FUNCTION, + actualType, errorType); + } + foreachNode.nillableResultType = nextMethodReturnType; + foreachNode.varType = valueType; + return; } - break; + // fallthrough + case TypeTags.SEMANTIC_ERROR: + foreachNode.varType = symTable.semanticError; + foreachNode.resultType = symTable.semanticError; + foreachNode.nillableResultType = symTable.semanticError; + return; + default: + foreachNode.varType = symTable.semanticError; + foreachNode.resultType = symTable.semanticError; + foreachNode.nillableResultType = symTable.semanticError; + dlog.error(foreachNode.collection.pos, DiagnosticErrorCode.ITERABLE_NOT_SUPPORTED_COLLECTION, + collectionType); + return; } - return false; - } - public boolean isAllErrorMembers(BUnionType actualType) { - return actualType.getMemberTypes().stream().allMatch(t -> isAssignable(t, symTable.errorType)); + BInvokableSymbol iteratorSymbol = (BInvokableSymbol) symResolver.lookupLangLibMethod(collectionType, + Names.fromString(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC), env); + BObjectType objectType = (BObjectType) getImpliedType(iteratorSymbol.retType); + BUnionType nextMethodReturnType = + (BUnionType) getResultTypeOfNextInvocation(objectType); + foreachNode.varType = varType; + foreachNode.resultType = getRecordType(nextMethodReturnType); + foreachNode.nillableResultType = nextMethodReturnType; } - public void setImplicitCastExpr(BLangExpression expr, BType actualType, BType targetType) { - BType expType = getImpliedType(targetType); - if (!isImplicitlyCastable(actualType, expType)) { + public void setInputClauseTypedBindingPatternType(BLangInputClause bLangInputClause) { + if (bLangInputClause.collection == null) { + //not-possible return; } - BLangTypeConversionExpr implicitConversionExpr = - (BLangTypeConversionExpr) TreeBuilder.createTypeConversionNode(); - implicitConversionExpr.pos = expr.pos; - implicitConversionExpr.expr = expr.impConversionExpr == null ? expr : expr.impConversionExpr; - implicitConversionExpr.setBType(expType); - implicitConversionExpr.targetType = expType; - implicitConversionExpr.internal = true; - expr.impConversionExpr = implicitConversionExpr; - } - public boolean checkListenerCompatibilityAtServiceDecl(BType type) { - if (getImpliedType(type).tag == TypeTags.UNION) { - // There should be at least one listener compatible type and all the member types, except error type - // should be listener compatible. - int listenerCompatibleTypeCount = 0; - for (BType memberType : getAllTypes(type, true)) { - if (memberType.tag != TypeTags.ERROR) { - if (!checkListenerCompatibility(memberType)) { - return false; - } - listenerCompatibleTypeCount++; - } - } - return listenerCompatibleTypeCount > 0; + BType collectionType = bLangInputClause.collection.getBType(); + bLangInputClause.varType = visitCollectionType(bLangInputClause, collectionType); + if (bLangInputClause.varType.tag == TypeTags.SEMANTIC_ERROR || + getImpliedType(collectionType).tag == OBJECT) { + return; } - return checkListenerCompatibility(type); + + BInvokableSymbol iteratorSymbol = (BInvokableSymbol) symResolver.lookupLangLibMethod(collectionType, + Names.fromString(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC), env); + BUnionType nextMethodReturnType = + (BUnionType) getResultTypeOfNextInvocation((BObjectType) getImpliedType(iteratorSymbol.retType)); + bLangInputClause.resultType = getRecordType(nextMethodReturnType); + bLangInputClause.nillableResultType = nextMethodReturnType; } - public boolean checkListenerCompatibility(BType bType) { - BType type = getImpliedType(bType); - if (type.tag == TypeTags.UNION) { - BUnionType unionType = (BUnionType) type; - for (BType memberType : unionType.getMemberTypes()) { - if (!checkListenerCompatibility(memberType)) { - return false; - } - } - return true; + private BType getTypedBindingPatternTypeForXmlCollection(BType collectionType) { + BType constraint = getImpliedType(((BXMLType) collectionType).constraint); + while (constraint.tag == TypeTags.XML) { + collectionType = constraint; + constraint = getImpliedType(((BXMLType) collectionType).constraint); } - if (type.tag != TypeTags.OBJECT) { - return false; + switch (constraint.tag) { + case TypeTags.XML_ELEMENT: + case TypeTags.XML_COMMENT: + case TypeTags.XML_TEXT: + case TypeTags.XML_PI: + case TypeTags.NEVER: + return constraint; + case TypeTags.UNION: + Set collectionTypes = getEffectiveMemberTypes((BUnionType) constraint); + Set builtinXMLConstraintTypes = getEffectiveMemberTypes + ((BUnionType) ((BXMLType) symTable.xmlType).constraint); + return collectionTypes.size() == 4 && builtinXMLConstraintTypes.equals(collectionTypes) ? + collectionType : + BUnionType.create(typeEnv(), null, (LinkedHashSet) collectionTypes); + default: + return null; } - - BObjectType rhsType = (BObjectType) type; - List rhsFuncs = ((BStructureTypeSymbol) rhsType.tsymbol).attachedFuncs; - - ListenerValidationModel listenerValidationModel = new ListenerValidationModel(this, symTable); - return listenerValidationModel.checkMethods(rhsFuncs); - - } - - public boolean isValidErrorDetailType(BType detailType) { - return switch (getImpliedType(detailType).tag) { - case TypeTags.MAP, TypeTags.RECORD -> isAssignable(detailType, symTable.detailType); - default -> false; - }; - } - - // private methods - - private boolean isSealedRecord(BType recordType) { - return recordType.getKind() == TypeKind.RECORD && ((BRecordType) recordType).sealed; - } - - private boolean isNullable(BType fieldType) { - return fieldType.isNullable(); } - private class BSameTypeVisitor implements BTypeVisitor { - - Set unresolvedTypes; - - TypeEqualityPredicate equality; - - BSameTypeVisitor(Set unresolvedTypes, TypeEqualityPredicate equality) { - this.unresolvedTypes = unresolvedTypes; - this.equality = equality; - } - - @Override - public Boolean visit(BType target, BType source) { - BType t = getImpliedType(target); - BType s = getImpliedType(source); - if (t == s) { - return true; - } - - return switch (t.tag) { - case TypeTags.INT, - TypeTags.BYTE, - TypeTags.FLOAT, - TypeTags.DECIMAL, - TypeTags.STRING, - TypeTags.BOOLEAN -> t.tag == s.tag && - ((TypeParamAnalyzer.isTypeParam(t) || TypeParamAnalyzer.isTypeParam(s)) || - (t.tag == TypeTags.TYPEREFDESC || s.tag == TypeTags.TYPEREFDESC)); - case TypeTags.ANY, - TypeTags.ANYDATA -> t.tag == s.tag && hasSameReadonlyFlag(s, t) && - (TypeParamAnalyzer.isTypeParam(t) || TypeParamAnalyzer.isTypeParam(s)); - default -> false; - }; - } - - @Override - public Boolean visit(BBuiltInRefType t, BType s) { - return t == s; - } - - @Override - public Boolean visit(BAnyType t, BType s) { - return t == s; - } - - @Override - public Boolean visit(BAnydataType t, BType s) { - return t == s || t.tag == s.tag; - } - - @Override - public Boolean visit(BMapType t, BType s) { - return s.tag == TypeTags.MAP && hasSameReadonlyFlag(s, t) && - equality.test(((BMapType) s).constraint, t.constraint, this.unresolvedTypes); - } - - @Override - public Boolean visit(BFutureType t, BType s) { - return s.tag == TypeTags.FUTURE && - equality.test(((BFutureType) s).constraint, t.constraint, this.unresolvedTypes); - } - - @Override - public Boolean visit(BXMLType t, BType s) { - return visit((BBuiltInRefType) t, s); - } - - @Override - public Boolean visit(BJSONType t, BType s) { - return s.tag == TypeTags.JSON && hasSameReadonlyFlag(s, t); - } - - @Override - public Boolean visit(BArrayType t, BType s) { - return s.tag == TypeTags.ARRAY && hasSameReadonlyFlag(s, t) && isSameArrayType(s, t, this.unresolvedTypes); - } - - @Override - public Boolean visit(BObjectType t, BType s) { - return t == s || (s.tag == TypeTags.OBJECT && t.tsymbol.pkgID.equals(s.tsymbol.pkgID) && - t.tsymbol.name.equals(s.tsymbol.name)); - } - - @Override - public Boolean visit(BRecordType t, BType s) { - if (t == s) { - return true; - } - - if (s.tag != TypeTags.RECORD || !hasSameReadonlyFlag(s, t)) { - return false; - } - - BRecordType source = (BRecordType) s; - LinkedHashMap sFields = source.fields; - LinkedHashMap tFields = t.fields; - - if (sFields.size() != tFields.size()) { - return false; - } - - for (BField sourceField : sFields.values()) { - if (tFields.containsKey(sourceField.name.value)) { - BField targetField = tFields.get(sourceField.name.value); - if ((!Symbols.isFlagOn(targetField.symbol.flags, Flags.READONLY) || - Symbols.isFlagOn(sourceField.symbol.flags, Flags.READONLY)) && - hasSameOptionalFlag(sourceField.symbol, targetField.symbol) && - equality.test(sourceField.type, targetField.type, new HashSet<>(this.unresolvedTypes))) { - continue; - } - } - return false; - } - return equality.test(source.restFieldType, t.restFieldType, new HashSet<>(this.unresolvedTypes)); - } - - private boolean hasSameOptionalFlag(BVarSymbol s, BVarSymbol t) { - return ((s.flags & Flags.OPTIONAL) ^ (t.flags & Flags.OPTIONAL)) != Flags.OPTIONAL; - } - - boolean hasSameReadonlyFlag(BType source, BType target) { - return Symbols.isFlagOn(target.flags, Flags.READONLY) == Symbols.isFlagOn(source.flags, Flags.READONLY); - } - - @Override - public Boolean visit(BTupleType t, BType s) { - List tTupleTypes = t.getTupleTypes(); - if (((!tTupleTypes.isEmpty() && checkAllTupleMembersBelongNoType(tTupleTypes)) || - (t.restType != null && t.restType.tag == TypeTags.NONE)) && - !(s.tag == TypeTags.ARRAY && ((BArrayType) s).state == BArrayState.OPEN)) { - return true; - } - - if (s.tag != TypeTags.TUPLE || !hasSameReadonlyFlag(s, t)) { - return false; - } - - BTupleType source = (BTupleType) s; - List sTupleTypes = source.getTupleTypes(); - if (sTupleTypes.size() != tTupleTypes.size()) { - return false; - } - - BType sourceRestType = source.restType; - BType targetRestType = t.restType; - if ((sourceRestType == null || targetRestType == null) && sourceRestType != targetRestType) { - return false; - } - - for (int i = 0; i < sTupleTypes.size(); i++) { - if (tTupleTypes.get(i) == symTable.noType) { - continue; - } - if (!equality.test(sTupleTypes.get(i), tTupleTypes.get(i), - new HashSet<>(this.unresolvedTypes))) { - return false; + private BType visitCollectionType(BLangInputClause bLangInputClause, BType collectionType) { + collectionType = getImpliedType(collectionType); + switch (collectionType.tag) { + case TypeTags.STRING: + return symTable.stringType; + case TypeTags.ARRAY: + BArrayType arrayType = (BArrayType) collectionType; + return arrayType.eType; + case TypeTags.TUPLE: + return getTupleMemberType((BTupleType) collectionType); + case TypeTags.MAP: + BMapType bMapType = (BMapType) collectionType; + return bMapType.constraint; + case TypeTags.RECORD: + BRecordType recordType = (BRecordType) collectionType; + return inferRecordFieldType(recordType); + case TypeTags.XML: + BType bindingPatternType = getTypedBindingPatternTypeForXmlCollection(collectionType); + return bindingPatternType == null ? symTable.semanticError : bindingPatternType; + case TypeTags.XML_TEXT: + return symTable.xmlTextType; + case TypeTags.TABLE: + BTableType tableType = (BTableType) collectionType; + return tableType.constraint; + case TypeTags.STREAM: + BStreamType streamType = (BStreamType) collectionType; + if (streamType.constraint.tag == TypeTags.NONE) { + return symTable.anydataType; } - } - - if (sourceRestType == null || targetRestType == symTable.noType) { - return true; - } - - return equality.test(sourceRestType, targetRestType, new HashSet<>(this.unresolvedTypes)); - } - - @Override - public Boolean visit(BStreamType t, BType s) { - if (s.tag != TypeTags.STREAM) { - return false; - } - - BStreamType source = (BStreamType) s; - return equality.test(source.constraint, t.constraint, unresolvedTypes) - && equality.test(source.completionType, t.completionType, unresolvedTypes); - } - - @Override - public Boolean visit(BTableType t, BType s) { - return t == s; - } - - @Override - public Boolean visit(BInvokableType t, BType s) { - return s.tag == TypeTags.INVOKABLE && - checkFunctionTypeEquality((BInvokableType) s, t, this.unresolvedTypes, equality); + return streamType.constraint; + case TypeTags.OBJECT: + // check for iterable objects + if (!isAssignable(collectionType, symTable.iterableType)) { + dlog.error(bLangInputClause.collection.pos, DiagnosticErrorCode.INVALID_ITERABLE_OBJECT_TYPE, + bLangInputClause.collection.getBType(), symTable.iterableType); + bLangInputClause.varType = symTable.semanticError; + bLangInputClause.resultType = symTable.semanticError; + bLangInputClause.nillableResultType = symTable.semanticError; + break; + } + + BUnionType nextMethodReturnType = getVarTypeFromIterableObject((BObjectType) collectionType); + if (nextMethodReturnType != null) { + bLangInputClause.resultType = getRecordType(nextMethodReturnType); + bLangInputClause.nillableResultType = nextMethodReturnType; + bLangInputClause.varType = ((BRecordType) bLangInputClause.resultType).fields.get("value").type; + return bLangInputClause.varType; + } + // fallthrough + case TypeTags.SEMANTIC_ERROR: + bLangInputClause.varType = symTable.semanticError; + bLangInputClause.resultType = symTable.semanticError; + bLangInputClause.nillableResultType = symTable.semanticError; + break; + default: + bLangInputClause.varType = symTable.semanticError; + bLangInputClause.resultType = symTable.semanticError; + bLangInputClause.nillableResultType = symTable.semanticError; + dlog.error(bLangInputClause.collection.pos, DiagnosticErrorCode.ITERABLE_NOT_SUPPORTED_COLLECTION, + collectionType); } + return symTable.semanticError; + } - @Override - public Boolean visit(BUnionType tUnionType, BType s) { - if (s.tag != TypeTags.UNION || !hasSameReadonlyFlag(s, tUnionType)) { - return false; - } - - BUnionType sUnionType = (BUnionType) s; - - if (sUnionType.getMemberTypes().size() - != tUnionType.getMemberTypes().size()) { - return false; - } - - Set sourceTypes = new LinkedHashSet<>(sUnionType.getMemberTypes().size()); - Set targetTypes = new LinkedHashSet<>(tUnionType.getMemberTypes().size()); - - if (sUnionType.isCyclic) { - sourceTypes.add(sUnionType); - } - if (tUnionType.isCyclic) { - targetTypes.add(tUnionType); - } - - sourceTypes.addAll(sUnionType.getMemberTypes()); - targetTypes.addAll(tUnionType.getMemberTypes()); - - boolean notSameType = sourceTypes - .stream() - .map(sT -> targetTypes - .stream() - .anyMatch(it -> equality.test(sT, it, new HashSet<>(this.unresolvedTypes)))) - .anyMatch(foundSameType -> !foundSameType); - return !notSameType; + private BType getTupleMemberType(BTupleType tupleType) { + LinkedHashSet tupleTypes = new LinkedHashSet<>(tupleType.getTupleTypes()); + if (tupleType.restType != null) { + tupleTypes.add(tupleType.restType); } - - @Override - public Boolean visit(BIntersectionType tIntersectionType, BType s) { - if (s.tag != TypeTags.INTERSECTION) { - return false; - } - return visit(tIntersectionType.effectiveType, ((BIntersectionType) s).effectiveType); + int tupleTypesSize = tupleTypes.size(); + if (tupleTypesSize == 0) { + return symTable.neverType; } + return tupleTypesSize == 1 ? + tupleTypes.iterator().next() : BUnionType.create(typeEnv(), null, tupleTypes); + } - @Override - public Boolean visit(BErrorType t, BType s) { - if (s.tag != TypeTags.ERROR) { - return false; - } - BErrorType source = (BErrorType) s; - - if (!source.typeIdSet.equals(t.typeIdSet)) { - return false; - } - - if (source.detailType == t.detailType) { - return true; + public BUnionType getVarTypeFromIterableObject(BObjectType collectionType) { + BObjectTypeSymbol objectTypeSymbol = (BObjectTypeSymbol) collectionType.tsymbol; + for (BAttachedFunction func : objectTypeSymbol.attachedFuncs) { + if (func.funcName.value.equals(BLangCompilerConstants.ITERABLE_COLLECTION_ITERATOR_FUNC)) { + return getVarTypeFromIteratorFunc(func); } - - return equality.test(source.detailType, t.detailType, this.unresolvedTypes); } - @Override - public Boolean visit(BTypedescType t, BType s) { - if (s.tag != TypeTags.TYPEDESC) { - return false; - } - BTypedescType sType = ((BTypedescType) s); - return equality.test(sType.constraint, t.constraint, this.unresolvedTypes); - } + return null; + } - @Override - public Boolean visit(BFiniteType t, BType s) { - return s == t; + private BUnionType getVarTypeFromIteratorFunc(BAttachedFunction candidateIteratorFunc) { + if (!candidateIteratorFunc.type.paramTypes.isEmpty()) { + return null; } - @Override - public Boolean visit(BParameterizedType t, BType s) { - if (s.tag != TypeTags.PARAMETERIZED_TYPE) { - return false; - } + BType returnType = candidateIteratorFunc.type.retType; + // abstract object {public function next() returns record {|int value;|}?;} + return getVarTypeFromIteratorFuncReturnType(returnType); + } - BParameterizedType sType = (BParameterizedType) s; - return sType.paramSymbol.equals(t.paramSymbol) && - equality.test(sType.paramValueType, t.paramValueType, new HashSet<>()); + public BUnionType getVarTypeFromIteratorFuncReturnType(BType type) { + BObjectTypeSymbol objectTypeSymbol; + BType returnType = getImpliedType(type); + if (returnType.tag != TypeTags.OBJECT) { + return null; } - public Boolean visit(BTypeReferenceType t, BType s) { - BType constraint = s; - if (s.tag == TypeTags.TYPEREFDESC) { - constraint = getImpliedType(((BTypeReferenceType) s).referredType); + objectTypeSymbol = (BObjectTypeSymbol) returnType.tsymbol; + for (BAttachedFunction func : objectTypeSymbol.attachedFuncs) { + if (func.funcName.value.equals(BLangCompilerConstants.NEXT_FUNC)) { + return getVarTypeFromNextFunc(func); } - BType target = getImpliedType(t.referredType); - return equality.test(target, constraint, new HashSet<>()); } - } - @Deprecated - public boolean isSameBIRShape(BType source, BType target) { - return isSameBIRShape(source, target, new HashSet<>()); + return null; } - private boolean isSameBIRShape(BType source, BType target, Set unresolvedTypes) { - // If we encounter two types that we are still resolving, then skip it. - // This is done to avoid recursive checking of the same type. - TypePair pair = new TypePair(source, target); - if (!unresolvedTypes.add(pair)) { - return true; + private BUnionType getVarTypeFromNextFunc(BAttachedFunction nextFunc) { + BType returnType; + if (!nextFunc.type.paramTypes.isEmpty()) { + return null; } - BIRSameShapeVisitor birSameShapeVisitor = new BIRSameShapeVisitor(unresolvedTypes, this::isSameBIRShape); - - if (target.accept(birSameShapeVisitor, source)) { - return true; + returnType = nextFunc.type.retType; + // Check if the next function return type has the union type, + // record {|int value;|}|error|(); + if (checkNextFuncReturnType(returnType)) { + return (BUnionType) returnType; } - unresolvedTypes.remove(pair); - return false; + return null; } - @Deprecated - private class BIRSameShapeVisitor extends BSameTypeVisitor { - - BIRSameShapeVisitor(Set unresolvedTypes, TypeEqualityPredicate equality) { - super(unresolvedTypes, equality); + private boolean checkNextFuncReturnType(BType returnType) { + if (getImpliedType(returnType).tag != TypeTags.UNION) { + return false; } - @Override - public Boolean visit(BType target, BType source) { - if (source.tag == TypeTags.TYPEREFDESC || target.tag == TypeTags.TYPEREFDESC) { - if (source.tag != target.tag) { - return false; - } - - BTypeReferenceType sourceRefType = (BTypeReferenceType) source; - BTypeReferenceType targetRefType = (BTypeReferenceType) target; - - BTypeSymbol sourceTSymbol = sourceRefType.tsymbol; - BTypeSymbol targetTSymbol = targetRefType.tsymbol; - String sourcePkgId = CompilerUtils.getPackageIDStringWithMajorVersion(sourceTSymbol.pkgID); - String targetPkgId = CompilerUtils.getPackageIDStringWithMajorVersion(targetTSymbol.pkgID); - return sourcePkgId.equals(targetPkgId) && sourceTSymbol.name.equals(targetTSymbol.name); - } - - BType t = getImpliedType(target); - BType s = getImpliedType(source); - if (t == s) { - return true; - } - return switch (t.tag) { - case TypeTags.INT, - TypeTags.BYTE, - TypeTags.FLOAT, - TypeTags.DECIMAL, - TypeTags.STRING, - TypeTags.BOOLEAN -> t.tag == s.tag && - ((TypeParamAnalyzer.isTypeParam(t) || TypeParamAnalyzer.isTypeParam(s)) || - (t.tag == TypeTags.TYPEREFDESC || s.tag == TypeTags.TYPEREFDESC)); - case TypeTags.ANY, - TypeTags.ANYDATA -> t.tag == s.tag && hasSameReadonlyFlag(s, t) && - (TypeParamAnalyzer.isTypeParam(t) || TypeParamAnalyzer.isTypeParam(s)); - default -> false; - }; - - } - - @Override - public Boolean visit(BFiniteType t, BType s) { - s = getImpliedType(s); - if (s.tag != TypeTags.FINITE) { - return false; - } - - Set sourceValueSpace = ((BFiniteType) s).getValueSpace(); - Set targetValueSpace = t.getValueSpace(); - - if (sourceValueSpace.size() != targetValueSpace.size()) { - return false; - } - - return hasSameMembers(sourceValueSpace, targetValueSpace); + List types = getAllTypes(returnType, true); + boolean containsCompletionType = types.removeIf(type -> type.tag == TypeTags.NIL); + containsCompletionType = types.removeIf(type -> type.tag == TypeTags.ERROR) || containsCompletionType; + if (!containsCompletionType) { + return false; } - @Override - public Boolean visit(BTypeReferenceType t, BType s) { - s = getImpliedType(s); - if (s.tag != TypeTags.TYPEREFDESC) { - return false; - } - - BTypeReferenceType sTypeRefType = (BTypeReferenceType) s; - - BTypeSymbol sourceTSymbol = sTypeRefType.tsymbol; - BTypeSymbol targetTSymbol = t.tsymbol; - String sourcePkgId = CompilerUtils.getPackageIDStringWithMajorVersion(sourceTSymbol.pkgID); - String targetPkgId = CompilerUtils.getPackageIDStringWithMajorVersion(targetTSymbol.pkgID); - return sourcePkgId.equals(targetPkgId) && sourceTSymbol.name.equals(targetTSymbol.name); + if (types.size() != 1) { + //TODO: print error + return false; } - } - - private boolean hasSameMembers(Set sourceValueSpace, Set targetValueSpace) { - Set setOne = new HashSet<>(sourceValueSpace); - Set setTwo = new HashSet<>(targetValueSpace); - - Iterator setOneIterator = setOne.iterator(); - Iterator setTwoIterator = setTwo.iterator(); - - while (setOneIterator.hasNext()) { - BLangLiteral setOneMem = (BLangLiteral) setOneIterator.next(); - - if (!setTwoIterator.hasNext()) { - return false; - } - - boolean hasEqualValue = false; - while (setTwoIterator.hasNext()) { - BLangLiteral setTwoMem = (BLangLiteral) setTwoIterator.next(); - if (setOneMem.value.equals(setTwoMem.value) && setOneMem.getBType() == setTwoMem.getBType()) { - hasEqualValue = true; - setOneIterator.remove(); - setTwoIterator.remove(); - break; - } - } - if (!hasEqualValue) { - return false; - } + if (types.get(0).tag != TypeTags.RECORD) { + return false; } - return !setTwoIterator.hasNext(); + BRecordType recordType = (BRecordType) types.get(0); + // Check if the union type has the record type, + // record {|int value;|}; + return checkRecordTypeInNextFuncReturnType(recordType); } - private class BOrderedTypeVisitor implements BTypeVisitor { - - Set unresolvedTypes; - - BOrderedTypeVisitor(Set unresolvedTypes) { - this.unresolvedTypes = unresolvedTypes; - } - - @Override - public Boolean visit(BType target, BType source) { - BType sourceType = getImpliedType(source); - BType targetType = getImpliedType(target); - int sourceTag = sourceType.tag; - int targetTag = targetType.tag; - if (isSimpleBasicType(sourceTag) && isSimpleBasicType(targetTag)) { - // If type T is ordered, then type T? Is also ordered. - return (source == target) || sourceTag == TypeTags.NIL || targetTag == TypeTags.NIL || - isIntOrStringType(sourceTag, targetTag); - } - if (sourceTag == TypeTags.FINITE) { - return checkValueSpaceHasSameType(((BFiniteType) source), target); - } - return isSameOrderedType(target, source, this.unresolvedTypes); - } - - @Override - public Boolean visit(BArrayType target, BType source) { - source = getImpliedType(source); - if (source.tag != TypeTags.ARRAY) { - if (source.tag == TypeTags.TUPLE || source.tag == TypeTags.UNION) { - return isSameOrderedType(target, source); - } - return false; - } - return isSameOrderedType(target.eType, ((BArrayType) source).eType, unresolvedTypes); - } - - @Override - public Boolean visit(BTupleType target, BType source) { - source = getImpliedType(source); - if (source.tag == TypeTags.UNION) { - return isSameOrderedType(target, source); - } - if (source.tag != TypeTags.TUPLE && source.tag != TypeTags.ARRAY) { - return false; - } - List targetTupleTypes = target.getTupleTypes(); - BType targetRestType = target.restType; - - if (source.tag == TypeTags.ARRAY) { - // Check whether the element type of the source array has same ordered type with each member type in - // target tuple type. - BType eType = ((BArrayType) source).eType; - for (BType memberType : targetTupleTypes) { - if (!isSameOrderedType(eType, memberType, this.unresolvedTypes)) { - return false; - } - } - if (targetRestType == null) { - return true; - } - return isSameOrderedType(targetRestType, eType, this.unresolvedTypes); - } - - BTupleType sourceT = (BTupleType) source; - List sourceTupleTypes = sourceT.getTupleTypes(); - - BType sourceRestType = sourceT.restType; - - int sourceTupleCount = sourceTupleTypes.size(); - int targetTupleCount = targetTupleTypes.size(); - - int len = Math.min(sourceTupleCount, targetTupleCount); - for (int i = 0; i < len; i++) { - // Check whether the corresponding member types are same ordered type. - if (!isSameOrderedType(sourceTupleTypes.get(i), targetTupleTypes.get(i), - this.unresolvedTypes)) { - return false; - } - } - - if (sourceTupleCount == targetTupleCount) { - if (sourceRestType == null || targetRestType == null) { - return true; - } - return isSameOrderedType(sourceRestType, targetRestType, this.unresolvedTypes); - } else if (sourceTupleCount > targetTupleCount) { - // Source tuple has higher number of member types. - // Check whether the excess member types can be narrowed to an ordered rest type in source tuple. - // Ex. source tuple -> [string, (), float, int, byte] - // target tuple -> [string, (), float] - // here, source tuple can be represented as [string, (), float, int...] - // since [string, (), float] & [string, (), float, int...] are individually order types and - // [string, (), float, int...] can be taken as the ordered type which the static type of both - // operands belong. - if (!hasCommonOrderedTypeForTuples(sourceTupleTypes, targetTupleCount + 1)) { - return false; - } - return checkSameOrderedTypeInTuples(sourceT, sourceTupleCount, targetTupleCount, sourceRestType, - targetRestType); - } else { - // Target tuple has higher number of member types. - if (!hasCommonOrderedTypeForTuples(targetTupleTypes, sourceTupleCount + 1)) { - return false; - } - return checkSameOrderedTypeInTuples(target, targetTupleCount, sourceTupleCount, targetRestType, - sourceRestType); - } - } - - private boolean hasCommonOrderedTypeForTuples(List typeList, int startIndex) { - BType baseType = typeList.get(startIndex - 1); - for (int i = startIndex; i < typeList.size(); i++) { - if (isNil(baseType)) { - baseType = typeList.get(i); - continue; - } - if (!isSameOrderedType(baseType, typeList.get(i), this.unresolvedTypes)) { - return false; - } - } - return true; + private boolean checkRecordTypeInNextFuncReturnType(BRecordType recordType) { + if (!recordType.sealed) { + return false; } - private boolean checkSameOrderedTypeInTuples(BTupleType source, int sourceTupleCount, - int targetTupleCount, - BType sourceRestType, BType targetRestType) { - if (targetRestType == null) { - return true; - } - for (int i = targetTupleCount; i < sourceTupleCount; i++) { - if (!isSameOrderedType(source.getTupleTypes().get(i), targetRestType, this.unresolvedTypes)) { - return false; - } - } - if (sourceRestType == null) { - return true; - } - return isSameOrderedType(sourceRestType, targetRestType, this.unresolvedTypes); + if (recordType.fields.size() != 1) { + return false; } - @Override - public Boolean visit(BUnionType target, BType source) { - source = getImpliedType(source); - if (source.tag != TypeTags.UNION || !hasSameReadonlyFlag(source, target)) { - return checkUnionHasSameType(target, source); - } - - BUnionType sUnionType = (BUnionType) source; - LinkedHashSet sourceTypes = sUnionType.getMemberTypes(); - LinkedHashSet targetTypes = target.getMemberTypes(); + return recordType.fields.containsKey(BLangCompilerConstants.VALUE_FIELD); + } - if (checkUnionHasAllFiniteOrNilMembers(sourceTypes) && - checkUnionHasAllFiniteOrNilMembers(targetTypes)) { - BType type = target.getMemberTypes().iterator().next(); - return checkValueSpaceHasSameType(((BFiniteType) getImpliedType(type)), - sUnionType.getMemberTypes().iterator().next()); + private BRecordType getRecordType(BUnionType type) { + for (BType member : type.getMemberTypes()) { + BType referredRecordType = getImpliedType(member); + if (referredRecordType.tag == TypeTags.RECORD) { + return (BRecordType) referredRecordType; } - - return checkSameOrderedTypesInUnionMembers(sourceTypes, targetTypes); } + return null; + } - private boolean checkSameOrderedTypesInUnionMembers(LinkedHashSet sourceTypes, - LinkedHashSet targetTypes) { + public BErrorType getErrorType(BUnionType type) { + for (BType member : type.getMemberTypes()) { + member = getImpliedType(member); - for (BType sourceT : sourceTypes) { - boolean foundSameOrderedType = false; - if (isNil(sourceT)) { - continue; - } - for (BType targetT : targetTypes) { - if (isNil(targetT)) { - foundSameOrderedType = true; - continue; - } - if (isSameOrderedType(targetT, sourceT, this.unresolvedTypes)) { - foundSameOrderedType = true; - } else { - return false; - } - } - if (!foundSameOrderedType) { - return false; + if (member.tag == TypeTags.ERROR) { + return (BErrorType) member; + } else if (member.tag == TypeTags.UNION) { + BErrorType e = getErrorType((BUnionType) member); + if (e != null) { + return e; } } - return true; - } - - @Override - public Boolean visit(BFiniteType t, BType s) { - return checkValueSpaceHasSameType(t, s); - } - - private boolean hasSameReadonlyFlag(BType source, BType target) { - return Symbols.isFlagOn(target.flags, Flags.READONLY) == Symbols.isFlagOn(source.flags, Flags.READONLY); - } - - @Override - public Boolean visit(BBuiltInRefType t, BType s) { - return false; - } - - @Override - public Boolean visit(BAnyType t, BType s) { - return false; - } - - @Override - public Boolean visit(BAnydataType t, BType s) { - return false; } + return null; + } - @Override - public Boolean visit(BMapType t, BType s) { - return false; - } + public BType getResultTypeOfNextInvocation(BObjectType iteratorType) { + BAttachedFunction nextFunc = getAttachedFuncFromObject(iteratorType, BLangCompilerConstants.NEXT_FUNC); + return Objects.requireNonNull(nextFunc).type.retType; + } - @Override - public Boolean visit(BFutureType t, BType s) { - return false; + public BAttachedFunction getAttachedFuncFromObject(BObjectType objectType, String funcName) { + BObjectTypeSymbol iteratorSymbol = (BObjectTypeSymbol) objectType.tsymbol; + for (BAttachedFunction bAttachedFunction : iteratorSymbol.attachedFuncs) { + if (funcName.equals(bAttachedFunction.funcName.value)) { + return bAttachedFunction; + } } + return null; + } - @Override - public Boolean visit(BXMLType t, BType s) { - return false; - } + public BType inferRecordFieldType(BRecordType recordType) { + Map fields = recordType.fields; + BUnionType unionType = BUnionType.create(typeEnv(), null); - @Override - public Boolean visit(BJSONType t, BType s) { - return false; + if (!recordType.sealed) { + unionType.add(recordType.restFieldType); + } else if (fields.isEmpty()) { + unionType.add(symTable.neverType); } + for (BField field : fields.values()) { + if (isAssignable(field.type, unionType)) { + continue; + } - @Override - public Boolean visit(BObjectType t, BType s) { - return false; - } + if (isAssignable(unionType, field.type)) { + unionType = BUnionType.create(typeEnv(), null); + } - @Override - public Boolean visit(BRecordType t, BType s) { - return false; + unionType.add(field.type); } - @Override - public Boolean visit(BStreamType t, BType s) { - return false; + if (unionType.getMemberTypes().size() > 1) { + unionType.tsymbol = Symbols.createTypeSymbol(SymTag.UNION_TYPE, Flags.asMask(EnumSet.of(Flag.PUBLIC)), + Names.EMPTY, recordType.tsymbol.pkgID, null, + recordType.tsymbol.owner, symTable.builtinPos, VIRTUAL); + return unionType; } - @Override - public Boolean visit(BTableType t, BType s) { - return false; - } + return unionType.getMemberTypes().iterator().next(); + } - @Override - public Boolean visit(BInvokableType t, BType s) { - return false; - } - @Override - public Boolean visit(BIntersectionType tIntersectionType, BType s) { - return this.visit(getImpliedType(tIntersectionType), s); + public BType getTypeWithEffectiveIntersectionTypes(BType bType) { + // TODO Can remove this method since this unwraps the referred type and intersection type. #40958 + BType type = getReferredType(bType); + BType effectiveType = null; + if (type.tag == TypeTags.INTERSECTION) { + effectiveType = ((BIntersectionType) type).effectiveType; + type = effectiveType; } - @Override - public Boolean visit(BErrorType t, BType s) { - return false; + if (type.tag != TypeTags.UNION) { + return Objects.requireNonNullElse(effectiveType, bType); } - @Override - public Boolean visit(BTypedescType t, BType s) { - return false; - } + LinkedHashSet members = new LinkedHashSet<>(); + boolean hasDifferentMember = false; - public Boolean visit(BTypeReferenceType t, BType s) { - return this.visit(getImpliedType(t), t); + for (BType memberType : ((BUnionType) type).getMemberTypes()) { + effectiveType = getTypeWithEffectiveIntersectionTypes(memberType); + effectiveType = getImpliedType(effectiveType); + if (effectiveType != memberType) { + hasDifferentMember = true; + } + members.add(effectiveType); } - @Override - public Boolean visit(BParameterizedType t, BType s) { - return false; + if (hasDifferentMember) { + return BUnionType.create(typeEnv(), null, members); } + return bType; } - private boolean isNil(BType type) { - // Currently, type reference for `null` literal is taken as Finite type and type reference for `()` literal - // taken as Nil type. - BType referredType = getImpliedType(type); - TypeKind referredTypeKind = referredType.getKind(); - if (referredTypeKind == TypeKind.NIL) { - return true; - } else if (referredTypeKind == TypeKind.FINITE) { - Set valueSpace = ((BFiniteType) referredType).getValueSpace(); - return valueSpace.size() == 1 && valueSpace.iterator().next().getBType().getKind() == TypeKind.NIL; - } - return false; + /** + * Enum to represent type test result. + * + * @since 1.2.0 + */ + enum TypeTestResult { + NOT_FOUND, + TRUE, + FALSE } - private boolean checkUnionHasSameType(BUnionType unionType, BType baseType) { - LinkedHashSet memberTypes = unionType.getMemberTypes(); - for (BType type : memberTypes) { - type = getImpliedType(type); - if (type.tag == TypeTags.FINITE) { - for (BLangExpression expr : ((BFiniteType) type).getValueSpace()) { - if (!isSameOrderedType(expr.getBType(), baseType)) { - return false; + TypeTestResult isBuiltInTypeWidenPossible(BType actualType, BType targetType) { // TODO: can we remove? + int targetTag = getImpliedType(targetType).tag; + int actualTag = getImpliedType(actualType).tag; + + if (actualTag < TypeTags.JSON && targetTag < TypeTags.JSON) { + // Fail Fast for value types. + switch (actualTag) { + case TypeTags.INT: + case TypeTags.BYTE: + case TypeTags.FLOAT: + case TypeTags.DECIMAL: + if (targetTag == TypeTags.BOOLEAN || targetTag == TypeTags.STRING) { + return TypeTestResult.FALSE; } + break; + case TypeTags.BOOLEAN: + if (targetTag == TypeTags.INT || targetTag == TypeTags.BYTE || targetTag == TypeTags.FLOAT + || targetTag == TypeTags.DECIMAL || targetTag == TypeTags.STRING) { + return TypeTestResult.FALSE; + } + break; + case TypeTags.STRING: + if (targetTag == TypeTags.INT || targetTag == TypeTags.BYTE || targetTag == TypeTags.FLOAT + || targetTag == TypeTags.DECIMAL || targetTag == TypeTags.BOOLEAN) { + return TypeTestResult.FALSE; + } + break; + } + } + switch (actualTag) { + case TypeTags.INT: + case TypeTags.BYTE: + case TypeTags.FLOAT: + case TypeTags.DECIMAL: + case TypeTags.BOOLEAN: + case TypeTags.STRING: + case TypeTags.SIGNED32_INT: + case TypeTags.SIGNED16_INT: + case TypeTags.SIGNED8_INT: + case TypeTags.UNSIGNED32_INT: + case TypeTags.UNSIGNED16_INT: + case TypeTags.UNSIGNED8_INT: + case TypeTags.CHAR_STRING: + if (targetTag == TypeTags.JSON || targetTag == TypeTags.ANYDATA || targetTag == TypeTags.ANY || + targetTag == TypeTags.READONLY) { + return TypeTestResult.TRUE; } -// } else if (type.tag == TypeTags.UNION) { -// if (!checkUnionHasSameType((BUnionType) type, baseType)) { -// return false; -// } - } else if (type.tag == TypeTags.TUPLE || type.tag == TypeTags.ARRAY) { - if (!isSameOrderedType(type, baseType)) { - return false; - } - } else if (isSimpleBasicType(type.tag)) { - if (!isSameOrderedType(type, baseType) && !isNil(type)) { - return false; + break; + case TypeTags.ANYDATA: + case TypeTags.TYPEDESC: + if (targetTag == TypeTags.ANY) { + return TypeTestResult.TRUE; } - } + break; + default: } - return true; - } - private boolean checkValueSpaceHasSameType(BFiniteType finiteType, BType type) { - BType baseType = getImpliedType(type); - if (baseType.tag == TypeTags.FINITE) { - BType baseExprType = finiteType.getValueSpace().iterator().next().getBType(); - return checkValueSpaceHasSameType(((BFiniteType) baseType), baseExprType); + if (TypeTags.isIntegerTypeTag(targetTag) && actualTag == targetTag) { + return TypeTestResult.FALSE; // No widening. } - boolean isValueSpaceSameType = false; - for (BLangExpression expr : finiteType.getValueSpace()) { - isValueSpaceSameType = isSameOrderedType(expr.getBType(), baseType); - if (!isValueSpaceSameType) { - break; - } + + // Validate for Integers subtypes. + if ((TypeTags.isIntegerTypeTag(actualTag) || actualTag == TypeTags.BYTE) + && (TypeTags.isIntegerTypeTag(targetTag) || targetTag == TypeTags.BYTE)) { + return checkBuiltInIntSubtypeWidenPossible(actualType, targetType); } - return isValueSpaceSameType; - } - private boolean checkUnionHasAllFiniteOrNilMembers(LinkedHashSet memberTypes) { - for (BType bType : memberTypes) { - BType type = getImpliedType(bType); - if (type.tag != TypeTags.FINITE && !isNil(type)) { - return false; - } + if (actualTag == TypeTags.CHAR_STRING && TypeTags.STRING == targetTag) { + return TypeTestResult.TRUE; } - return true; + return TypeTestResult.NOT_FOUND; } - private boolean checkFieldEquivalency(BRecordType lhsType, BRecordType rhsType, Set unresolvedTypes) { - Map rhsFields = new LinkedHashMap<>(rhsType.fields); - - // Check if the RHS record has corresponding fields to those of the LHS record. - for (BField lhsField : lhsType.fields.values()) { - BField rhsField = rhsFields.get(lhsField.name.value); - - // If LHS field is required, there should be a corresponding RHS field - // If LHS field is never typed, RHS rest field type should include never type - if (rhsField == null) { - if (!Symbols.isOptional(lhsField.symbol) || isInvalidNeverField(lhsField, rhsType)) { - return false; + private TypeTestResult checkBuiltInIntSubtypeWidenPossible(BType actualType, BType targetType) { + int actualTag = getImpliedType(actualType).tag; + targetType = getImpliedType(targetType); + switch (targetType.tag) { + case TypeTags.INT: + if (actualTag == TypeTags.BYTE || TypeTags.isIntegerTypeTag(actualTag)) { + return TypeTestResult.TRUE; } - - if (!rhsType.sealed && !isAssignable(rhsType.restFieldType, lhsField.type, unresolvedTypes)) { - return false; + break; + case TypeTags.SIGNED32_INT: + if (actualTag == TypeTags.SIGNED16_INT || actualTag == TypeTags.SIGNED8_INT || + actualTag == TypeTags.UNSIGNED16_INT || actualTag == TypeTags.UNSIGNED8_INT || + actualTag == TypeTags.BYTE) { + return TypeTestResult.TRUE; } - - continue; - } - if (hasIncompatibleReadOnlyFlags(lhsField.symbol.flags, rhsField.symbol.flags)) { - return false; - } - - // If LHS field is required, so should the RHS field - if (!Symbols.isOptional(lhsField.symbol) && Symbols.isOptional(rhsField.symbol)) { - return false; - } - - // The corresponding RHS field should be assignable to the LHS field. - if (!isAssignable(rhsField.type, lhsField.type, unresolvedTypes)) { - return false; - } - - rhsFields.remove(lhsField.name.value); - } - - if (lhsType.sealed) { - for (BField field : rhsFields.values()) { - if (!isNeverTypeOrStructureTypeWithARequiredNeverMember(field.type)) { - return false; + break; + case TypeTags.SIGNED16_INT: + if (actualTag == TypeTags.SIGNED8_INT || actualTag == TypeTags.UNSIGNED8_INT || + actualTag == TypeTags.BYTE) { + return TypeTestResult.TRUE; + } + break; + case TypeTags.UNSIGNED32_INT: + if (actualTag == TypeTags.UNSIGNED16_INT || actualTag == TypeTags.UNSIGNED8_INT || + actualTag == TypeTags.BYTE) { + return TypeTestResult.TRUE; + } + break; + case TypeTags.UNSIGNED16_INT: + if (actualTag == TypeTags.UNSIGNED8_INT || actualTag == TypeTags.BYTE) { + return TypeTestResult.TRUE; + } + break; + case TypeTags.BYTE: + if (actualTag == TypeTags.UNSIGNED8_INT) { + return TypeTestResult.TRUE; + } + break; + case TypeTags.UNSIGNED8_INT: + if (actualTag == TypeTags.BYTE) { + return TypeTestResult.TRUE; } - } - return true; - } - - // If there are any remaining RHS fields, the types of those should be assignable to the rest field type of - // the LHS record. - BType lhsRestFieldType = lhsType.restFieldType; - for (BField field : rhsFields.values()) { - if (!isAssignable(field.type, lhsRestFieldType, unresolvedTypes)) { - return false; - } } - return true; + return TypeTestResult.NOT_FOUND; } - private boolean isInvalidNeverField(BField lhsField, BRecordType rhsType) { - if (getImpliedType(lhsField.type).tag != NEVER || rhsType.sealed) { - return false; + public boolean isImplicitlyCastable(BType actual, BType target) { + /* The word Builtin refers for Compiler known types. */ + + BType targetType = getImpliedType(target); + BType actualType = getImpliedType(actual); + BType newTargetType = targetType; + int targetTypeTag = targetType.tag; + if ((targetTypeTag == TypeTags.UNION || targetTypeTag == TypeTags.FINITE) && isValueType(actualType)) { + newTargetType = symTable.anyType; // TODO : Check for correctness. } - BType restFieldType = rhsType.restFieldType; - return switch (restFieldType.tag) { - case TypeTags.UNION -> { - for (BType member : ((BUnionType) restFieldType).getOriginalMemberTypes()) { - if (getImpliedType(member).tag == NEVER) { - yield false; - } - } - yield true; - } - case NEVER -> false; - default -> true; - }; - } + TypeTestResult result = isBuiltInTypeWidenPossible(actualType, newTargetType); + if (result != TypeTestResult.NOT_FOUND) { + return result == TypeTestResult.TRUE; + } - private Optional getMatchingInvokableType(List rhsFuncList, - BAttachedFunction lhsFunc, - Set unresolvedTypes) { - Optional matchingFunction = rhsFuncList.stream() - .filter(rhsFunc -> lhsFunc.funcName.equals(rhsFunc.funcName)) - .filter(rhsFunc -> isFunctionTypeAssignable(rhsFunc.type, lhsFunc.type, unresolvedTypes)) - .findFirst(); + if (isValueType(targetType) && + (actualType.tag == TypeTags.FINITE || + (actualType.tag == TypeTags.UNION && ((BUnionType) actualType).getMemberTypes().stream() + .anyMatch(type -> getImpliedType(type).tag == TypeTags.FINITE && + isAssignable(type, targetType))))) { + // for nil, no cast is required + return TypeTags.isIntegerTypeTag(targetTypeTag) || targetType.tag == TypeTags.BYTE || + targetTypeTag == TypeTags.FLOAT || + targetTypeTag == TypeTags.DECIMAL || + TypeTags.isStringTypeTag(targetTypeTag) || + targetTypeTag == TypeTags.BOOLEAN; - if (matchingFunction.isEmpty()) { - return matchingFunction; - } - // For resource function match, we need to check whether lhs function resource path type belongs to - // rhs function resource path type - BAttachedFunction matchingFunc = matchingFunction.get(); - // Todo: We could include this logic in `isFunctionTypeAssignable` if we have `resourcePathType` information in - // `BInvokableType` issue #37502 - boolean lhsFuncIsResource = Symbols.isResource(lhsFunc.symbol); - boolean matchingFuncIsResource = Symbols.isResource(matchingFunc.symbol); + } else if (isValueType(targetType) && actualType.tag == TypeTags.UNION && + ((BUnionType) actualType).getMemberTypes().stream().allMatch(type -> isAssignable(type, targetType))) { + return true; - if (!lhsFuncIsResource && !matchingFuncIsResource) { - return matchingFunction; + } else if (targetTypeTag == TypeTags.ERROR + && (actualType.tag == TypeTags.UNION + && isAllErrorMembers((BUnionType) actualType))) { + return true; } + return false; + } - if (!lhsFuncIsResource || !matchingFuncIsResource) { - return Optional.empty(); + public boolean isTypeCastable(BType source, BType target) { + BType sourceType = getImpliedType(source); + BType targetType = getImpliedType(target); + if (sourceType.tag == TypeTags.SEMANTIC_ERROR || targetType.tag == TypeTags.SEMANTIC_ERROR || + sourceType == targetType) { + return true; } - List lhsFuncPathTypes = ((BResourceFunction) lhsFunc).pathSegmentSymbols; - List rhsFuncPathTypes = ((BResourceFunction) matchingFunc).pathSegmentSymbols; + SemType sourceSemType = sourceType.semType(); + SemType targetSemType = targetType.semType(); - int lhsFuncResourcePathTypesSize = lhsFuncPathTypes.size(); - if (lhsFuncResourcePathTypesSize != rhsFuncPathTypes.size()) { - return Optional.empty(); + // Disallow casting away error, this forces user to handle the error via type-test, check, or checkpanic + if (containsErrorType(sourceSemType) && !containsErrorType(targetSemType)) { + return false; } - for (int i = 0; i < lhsFuncResourcePathTypesSize; i++) { - if (!isAssignable(lhsFuncPathTypes.get(i).type, rhsFuncPathTypes.get(i).type)) { - return Optional.empty(); - } + if (isNumericConversionPossible(sourceType, targetType)) { + return true; } - return matchingFunction; + return intersectionExists(sourceSemType, targetSemType); } - private boolean isInSameVisibilityRegion(BSymbol lhsSym, BSymbol rhsSym) { - if (Symbols.isPrivate(lhsSym)) { - return Symbols.isPrivate(rhsSym) && lhsSym.pkgID.equals(rhsSym.pkgID) - && lhsSym.owner.name.equals(rhsSym.owner.name); - } else if (Symbols.isPublic(lhsSym)) { - return Symbols.isPublic(rhsSym); - } - return !Symbols.isPrivate(rhsSym) && !Symbols.isPublic(rhsSym) && lhsSym.pkgID.equals(rhsSym.pkgID); + public boolean containsErrorType(SemType t) { + return SemTypes.containsBasicType(t, PredefinedType.ERROR); } - private boolean isAssignableToUnionType(BType source, BType target, Set unresolvedTypes) { - TypePair pair = new TypePair(source, target); - if (unresolvedTypes.contains(pair)) { - return true; - } - - source = getImpliedType(source); - target = getImpliedType(target); - if (isJsonAnydataOrUserDefinedUnion(source) && ((BUnionType) source).isCyclic) { - // add cyclic source to target pair to avoid recursive calls - unresolvedTypes.add(pair); + boolean isNumericConversionPossible(BType sourceType, BType targetType) { + Optional targetNumericType = Core.singleNumericType(targetType.semType()); + if (targetNumericType.isEmpty()) { + return false; } - Set sourceTypes = new LinkedHashSet<>(); - Set targetTypes = new LinkedHashSet<>(); + return !Core.isEmpty(semTypeCtx, SemTypes.intersect(sourceType.semType(), PredefinedType.NUMBER)); + } - if (isJsonAnydataOrUserDefinedUnion(source)) { - sourceTypes.addAll(getEffectiveMemberTypes((BUnionType) source)); - } else { - sourceTypes.add(source); - } + public boolean isAllErrorMembers(BUnionType actualType) { + return isSubtype(actualType, PredefinedType.ERROR); + } - boolean targetIsAUnion = false; - if (target.tag == TypeTags.UNION) { - targetIsAUnion = true; - targetTypes.addAll(getEffectiveMemberTypes((BUnionType) target)); - } else { - targetTypes.add(target); + public void setImplicitCastExpr(BLangExpression expr, BType actualType, BType targetType) { + BType expType = getImpliedType(targetType); + if (!isImplicitlyCastable(actualType, expType)) { + return; } + BLangTypeConversionExpr implicitConversionExpr = + (BLangTypeConversionExpr) TreeBuilder.createTypeConversionNode(); + implicitConversionExpr.pos = expr.pos; + implicitConversionExpr.expr = expr.impConversionExpr == null ? expr : expr.impConversionExpr; + implicitConversionExpr.setBType(expType); + implicitConversionExpr.targetType = expType; + implicitConversionExpr.internal = true; + expr.impConversionExpr = implicitConversionExpr; + } - // check if all the value types are assignable between two unions - Iterator sourceIterator = sourceTypes.iterator(); - while (sourceIterator.hasNext()) { - BType sMember = sourceIterator.next(); - if (sMember.tag == TypeTags.NEVER) { - sourceIterator.remove(); - continue; - } - if (sMember.tag == TypeTags.FINITE && isAssignable(sMember, target, unresolvedTypes)) { - sourceIterator.remove(); - continue; - } - if (sMember.tag == TypeTags.XML && - isAssignableToUnionType(expandedXMLBuiltinSubtypes, target, unresolvedTypes)) { - sourceIterator.remove(); - continue; - } - - if (!isValueType(sMember)) { - if (!targetIsAUnion) { - continue; - } - BUnionType targetUnion = (BUnionType) target; - // prevent cyclic unions being compared as individual items - if (sMember instanceof BUnionType sUnion) { - if (sUnion.isCyclic && targetUnion.isCyclic) { - unresolvedTypes.add(new TypePair(sUnion, targetUnion)); - if (isAssignable(sUnion, targetUnion, unresolvedTypes)) { - sourceIterator.remove(); - continue; - } - } - if (sMember.tag == TypeTags.JSON && isAssignable(sUnion, targetUnion, unresolvedTypes)) { - sourceIterator.remove(); - continue; + public boolean checkListenerCompatibilityAtServiceDecl(BType type) { + if (getImpliedType(type).tag == TypeTags.UNION) { + // There should be at least one listener compatible type and all the member types, except error type + // should be listener compatible. + int listenerCompatibleTypeCount = 0; + for (BType memberType : getAllTypes(type, true)) { + if (memberType.tag != TypeTags.ERROR) { + if (!checkListenerCompatibility(memberType)) { + return false; } + listenerCompatibleTypeCount++; } - // readonly can match to a union similar to any|error - if (sMember.tag == TypeTags.READONLY && isAssignable(symTable.anyAndReadonlyOrError, targetUnion)) { - sourceIterator.remove(); - continue; - } - continue; - } - - boolean sourceTypeIsNotAssignableToAnyTargetType = true; - Iterator targetIterator = targetTypes.iterator(); - while (targetIterator.hasNext()) { - BType t = targetIterator.next(); - if (isAssignable(sMember, t, unresolvedTypes)) { - sourceIterator.remove(); - sourceTypeIsNotAssignableToAnyTargetType = false; - break; - } - } - if (sourceTypeIsNotAssignableToAnyTargetType) { - unresolvedTypes.remove(pair); - return false; } + return listenerCompatibleTypeCount > 0; } + return checkListenerCompatibility(type); + } - // check the structural values for similarity - sourceIterator = sourceTypes.iterator(); - while (sourceIterator.hasNext()) { - BType sourceMember = sourceIterator.next(); - boolean sourceTypeIsNotAssignableToAnyTargetType = true; - Iterator targetIterator = targetTypes.iterator(); - - boolean selfReferencedSource = (sourceMember != source) && - isSelfReferencedStructuredType(source, sourceMember); - - while (targetIterator.hasNext()) { - BType targetMember = targetIterator.next(); - - boolean selfReferencedTarget = isSelfReferencedStructuredType(target, targetMember); - if (selfReferencedTarget && selfReferencedSource && (sourceMember.tag == targetMember.tag)) { - sourceTypeIsNotAssignableToAnyTargetType = false; - break; - } - - if (isAssignable(sourceMember, targetMember, unresolvedTypes)) { - sourceTypeIsNotAssignableToAnyTargetType = false; - break; + public boolean checkListenerCompatibility(BType bType) { + BType type = getImpliedType(bType); + if (type.tag == TypeTags.UNION) { + BUnionType unionType = (BUnionType) type; + for (BType memberType : unionType.getMemberTypes()) { + if (!checkListenerCompatibility(memberType)) { + return false; } } - if (sourceTypeIsNotAssignableToAnyTargetType) { - unresolvedTypes.remove(pair); - return false; - } + return true; } - unresolvedTypes.add(pair); - return true; - } + if (type.tag != TypeTags.OBJECT) { + return false; + } - private boolean isJsonAnydataOrUserDefinedUnion(BType type) { - int tag = type.tag; - return tag == TypeTags.UNION || tag == TypeTags.JSON || tag == TypeTags.ANYDATA; - } + BObjectType rhsType = (BObjectType) type; + List rhsFuncs = ((BStructureTypeSymbol) rhsType.tsymbol).attachedFuncs; - public boolean isSelfReferencedStructuredType(BType source, BType s) { - if (source == s) { - return true; - } + ListenerValidationModel listenerValidationModel = new ListenerValidationModel(this, symTable); + return listenerValidationModel.checkMethods(rhsFuncs); - s = getImpliedType(s); - if (s.tag == TypeTags.ARRAY) { - return isSelfReferencedStructuredType(source, ((BArrayType) s).eType); - } - if (s.tag == TypeTags.MAP) { - return isSelfReferencedStructuredType(source, ((BMapType) s).constraint); - } - if (s.tag == TypeTags.TABLE) { - return isSelfReferencedStructuredType(source, ((BTableType) s).constraint); - } - return false; } - public BType updateSelfReferencedWithNewType(BType source, BType s, BType target) { - if (s.tag == TypeTags.ARRAY) { - BArrayType arrayType = (BArrayType) s; - if (arrayType.eType == source) { - return new BArrayType(target, arrayType.tsymbol, arrayType.size, - arrayType.state, arrayType.flags); - } - } - if (s.tag == TypeTags.MAP) { - BMapType mapType = (BMapType) s; - if (mapType.constraint == source) { - return new BMapType(mapType.tag, target, mapType.tsymbol, mapType.flags); - } - } - if (s.tag == TypeTags.TABLE) { - BTableType tableType = (BTableType) s; - if (tableType.constraint == source) { - return new BTableType(tableType.tag, target, tableType.tsymbol, - tableType.flags); - } else if (tableType.constraint instanceof BMapType) { - return updateSelfReferencedWithNewType(source, tableType.constraint, target); - } - } - return s; + public boolean isValidErrorDetailType(BType detailType) { + return switch (getImpliedType(detailType).tag) { + case TypeTags.MAP, TypeTags.RECORD -> isAssignable(detailType, symTable.detailType); + default -> false; + }; } - public static void fixSelfReferencingSameUnion(BType originalMemberType, BUnionType origUnionType, - BType immutableMemberType, BUnionType newImmutableUnion, - LinkedHashSet readOnlyMemTypes) { - boolean sameMember = originalMemberType == immutableMemberType; - if (originalMemberType.tag == TypeTags.ARRAY) { - BArrayType arrayType = (BArrayType) originalMemberType; - if (origUnionType == arrayType.eType) { - if (sameMember) { - BArrayType newArrayType = new BArrayType(newImmutableUnion, arrayType.tsymbol, arrayType.size, - arrayType.state, arrayType.flags); - readOnlyMemTypes.add(newArrayType); - } else { - ((BArrayType) immutableMemberType).eType = newImmutableUnion; - readOnlyMemTypes.add(immutableMemberType); - } - } - } else if (originalMemberType.tag == TypeTags.MAP) { - BMapType mapType = (BMapType) originalMemberType; - if (origUnionType == mapType.constraint) { - if (sameMember) { - BMapType newMapType = new BMapType(mapType.tag, newImmutableUnion, mapType.tsymbol, mapType.flags); - readOnlyMemTypes.add(newMapType); - } else { - ((BMapType) immutableMemberType).constraint = newImmutableUnion; - readOnlyMemTypes.add(immutableMemberType); - } - } - } else if (originalMemberType.tag == TypeTags.TABLE) { - BTableType tableType = (BTableType) originalMemberType; - if (origUnionType == tableType.constraint) { - if (sameMember) { - BTableType newTableType = new BTableType(tableType.tag, newImmutableUnion, tableType.tsymbol, - tableType.flags); - readOnlyMemTypes.add(newTableType); - } else { - ((BTableType) immutableMemberType).constraint = newImmutableUnion; - readOnlyMemTypes.add(immutableMemberType); - } - return; - } - - BType immutableConstraint = ((BTableType) immutableMemberType).constraint; - if (tableType.constraint.tag == TypeTags.MAP) { - sameMember = tableType.constraint == immutableConstraint; - BMapType mapType = (BMapType) tableType.constraint; - if (origUnionType == mapType.constraint) { - if (sameMember) { - BMapType newMapType = new BMapType(mapType.tag, newImmutableUnion, mapType.tsymbol, - mapType.flags); - ((BTableType) immutableMemberType).constraint = newMapType; - } else { - ((BTableType) immutableMemberType).constraint = newImmutableUnion; - } - readOnlyMemTypes.add(immutableMemberType); - } - } - } else { - readOnlyMemTypes.add(immutableMemberType); - } - } + // private methods private Set getEffectiveMemberTypes(BUnionType unionType) { Set memTypes = new LinkedHashSet<>(); @@ -3880,44 +1803,6 @@ private Set getEffectiveMemberTypes(BUnionType unionType) { return memTypes; } - private boolean isFiniteTypeAssignable(BFiniteType finiteType, BType targetType, Set unresolvedTypes) { - BType expType = getImpliedType(targetType); - if (expType.tag == TypeTags.FINITE) { - for (BLangExpression expression : finiteType.getValueSpace()) { - ((BLangLiteral) expression).isFiniteContext = true; - if (!isAssignableToFiniteType(expType, (BLangLiteral) expression)) { - return false; - } - } - return true; - } - - if (expType.tag == TypeTags.UNION) { - List unionMemberTypes = getAllTypes(targetType, true); - for (BLangExpression valueExpr : finiteType.getValueSpace()) { - ((BLangLiteral) valueExpr).isFiniteContext = true; - if (unionMemberTypes.stream() - .noneMatch(targetMemType -> - getImpliedType(targetMemType).tag == TypeTags.FINITE ? - isAssignableToFiniteType(targetMemType, (BLangLiteral) valueExpr) : - isAssignable(valueExpr.getBType(), targetMemType, unresolvedTypes) || - isLiteralCompatibleWithBuiltinTypeWithSubTypes( - (BLangLiteral) valueExpr, targetMemType))) { - return false; - } - } - return true; - } - - for (BLangExpression expression : finiteType.getValueSpace()) { - if (!isLiteralCompatibleWithBuiltinTypeWithSubTypes((BLangLiteral) expression, targetType) && - !isAssignable(expression.getBType(), expType, unresolvedTypes)) { - return false; - } - } - return true; - } - boolean isAssignableToFiniteType(BType type, BLangLiteral literalExpr) { type = getImpliedType(type); if (type.tag != TypeTags.FINITE) { @@ -3925,20 +1810,7 @@ boolean isAssignableToFiniteType(BType type, BLangLiteral literalExpr) { } BFiniteType expType = (BFiniteType) type; - return expType.getValueSpace().stream().anyMatch(memberLiteral -> { - if (((BLangLiteral) memberLiteral).value == null) { - return literalExpr.value == null; - } - - // If the literal which needs to be tested is from finite type and the type of the any member literal - // is not the same type, the literal cannot be assignable to finite type. - if (literalExpr.isFiniteContext && memberLiteral.getBType().tag != literalExpr.getBType().tag) { - return false; - } - // Check whether the literal that needs to be tested is assignable to any of the member literal in the - // value space. - return checkLiteralAssignabilityBasedOnType((BLangLiteral) memberLiteral, literalExpr); - }); + return checkLiteralAssignabilityBasedOnType(literalExpr, expType, literalExpr.getBType().tag); } /** @@ -3947,18 +1819,14 @@ boolean isAssignableToFiniteType(BType type, BLangLiteral literalExpr) { * literal or a constant. In case of a constant, it is assignable to the base literal if and only if both * literals have same type and equivalent values. * - * @param baseLiteral Literal based on which we check the assignability. - * @param candidateLiteral Literal to be tested whether it is assignable to the base literal or not. + * @param literal Literal to be tested whether it is assignable to the base literal or not. + * @param finiteType + * @param targetTypeTag * @return true if assignable; false otherwise. */ - boolean checkLiteralAssignabilityBasedOnType(BLangLiteral baseLiteral, BLangLiteral candidateLiteral) { - // Different literal kinds. - if (baseLiteral.getKind() != candidateLiteral.getKind()) { - return false; - } - Object baseValue = baseLiteral.value; - Object candidateValue = candidateLiteral.value; - int candidateTypeTag = candidateLiteral.getBType().tag; + boolean checkLiteralAssignabilityBasedOnType(BLangLiteral literal, BFiniteType finiteType, int targetTypeTag) { + Object value = literal.value; + int literalTypeTag = literal.getBType().tag; // Numeric literal assignability is based on assignable type and numeric equivalency of values. // If the base numeric literal is, @@ -3967,104 +1835,64 @@ boolean checkLiteralAssignabilityBasedOnType(BLangLiteral baseLiteral, BLangLite // (3) float: we can assign int simple literal(Not an int constant) or a float literal/constant with same value. // (4) decimal: we can assign int simple literal or float simple literal (Not int/float constants) or decimal // with the same value. - switch (baseLiteral.getBType().tag) { - case TypeTags.BYTE: - if (candidateTypeTag == TypeTags.BYTE || (candidateTypeTag == TypeTags.INT && - !candidateLiteral.isConstant && isByteLiteralValue((Long) candidateValue))) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; + SemType t = finiteType.semType(); + switch (targetTypeTag) { case TypeTags.INT: - if (candidateTypeTag == TypeTags.INT) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.SIGNED32_INT: - if (candidateTypeTag == TypeTags.INT && isSigned32LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.SIGNED16_INT: - if (candidateTypeTag == TypeTags.INT && isSigned16LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.SIGNED8_INT: - if (candidateTypeTag == TypeTags.INT && isSigned8LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.UNSIGNED32_INT: - if (candidateTypeTag == TypeTags.INT && isUnsigned32LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.UNSIGNED16_INT: - if (candidateTypeTag == TypeTags.INT && isUnsigned16LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); - } - break; - case TypeTags.UNSIGNED8_INT: - if (candidateTypeTag == TypeTags.INT && isUnsigned8LiteralValue((Long) candidateValue)) { - return ((Number) baseValue).longValue() == ((Number) candidateValue).longValue(); + if (literalTypeTag == TypeTags.INT) { + if (value instanceof String) { + return false; + } + return Core.containsConstInt(t, ((Number) value).longValue()); } break; case TypeTags.FLOAT: - String baseValueStr = String.valueOf(baseValue); - String originalValue = baseLiteral.originalValue != null ? baseLiteral.originalValue : baseValueStr; - if (NumericLiteralSupport.isDecimalDiscriminated(originalValue)) { - return false; - } - double baseDoubleVal; - try { - baseDoubleVal = Double.parseDouble(baseValueStr); - } catch (NumberFormatException e) { - // We reach here if a floating point literal has syntax diagnostics. - return false; - } - double candidateDoubleVal; - if (candidateTypeTag == TypeTags.INT && !candidateLiteral.isConstant) { - if (candidateLiteral.value instanceof Double) { + double doubleValue; + if (literalTypeTag == TypeTags.INT && !literal.isConstant) { + if (literal.value instanceof Double) { // Out of range value for int but in range for float - candidateDoubleVal = Double.parseDouble(String.valueOf(candidateValue)); - return baseDoubleVal == candidateDoubleVal; + doubleValue = Double.parseDouble(String.valueOf(value)); } else { - candidateDoubleVal = ((Long) candidateValue).doubleValue(); - return baseDoubleVal == candidateDoubleVal; + doubleValue = ((Long) value).doubleValue(); + } + return Core.containsConstFloat(t, doubleValue); + } else if (literalTypeTag == TypeTags.FLOAT) { + try { + doubleValue = Double.parseDouble(String.valueOf(value)); + return Core.containsConstFloat(t, doubleValue); + } catch (NumberFormatException e) { + return false; } - } else if (candidateTypeTag == TypeTags.FLOAT) { - candidateDoubleVal = Double.parseDouble(String.valueOf(candidateValue)); - return baseDoubleVal == candidateDoubleVal; } break; case TypeTags.DECIMAL: - BigDecimal baseDecimalVal = NumericLiteralSupport.parseBigDecimal(baseValue); - BigDecimal candidateDecimalVal; - if (candidateTypeTag == TypeTags.INT && !candidateLiteral.isConstant) { - if (candidateLiteral.value instanceof String) { + BigDecimal decimalValue; + if (literalTypeTag == TypeTags.INT && !literal.isConstant) { + if (literal.value instanceof String) { // out of range value for float but in range for decimal - candidateDecimalVal = NumericLiteralSupport.parseBigDecimal(candidateValue); - return baseDecimalVal.compareTo(candidateDecimalVal) == 0; - } else if (candidateLiteral.value instanceof Double) { + decimalValue = NumericLiteralSupport.parseBigDecimal(value); + } else if (literal.value instanceof Double) { // out of range value for int in range for decimal and float - candidateDecimalVal = new BigDecimal((Double) candidateValue, MathContext.DECIMAL128); - return baseDecimalVal.compareTo(candidateDecimalVal) == 0; + decimalValue = new BigDecimal((Double) value, MathContext.DECIMAL128); } else { - candidateDecimalVal = new BigDecimal((long) candidateValue, MathContext.DECIMAL128); - return baseDecimalVal.compareTo(candidateDecimalVal) == 0; + decimalValue = new BigDecimal((long) value, MathContext.DECIMAL128); } - } else if (candidateTypeTag == TypeTags.FLOAT && !candidateLiteral.isConstant || - candidateTypeTag == TypeTags.DECIMAL) { - if (NumericLiteralSupport.isFloatDiscriminated(String.valueOf(candidateValue))) { + return Core.containsConstDecimal(t, decimalValue); + } else if (literalTypeTag == TypeTags.FLOAT && !literal.isConstant || + literalTypeTag == TypeTags.DECIMAL) { + if (NumericLiteralSupport.isFloatDiscriminated(String.valueOf(value))) { + return false; + } + try { + decimalValue = NumericLiteralSupport.parseBigDecimal(value); + return Core.containsConstDecimal(t, decimalValue); + } catch (NumberFormatException e) { return false; } - candidateDecimalVal = NumericLiteralSupport.parseBigDecimal(candidateValue); - return baseDecimalVal.compareTo(candidateDecimalVal) == 0; } break; default: // Non-numeric literal kind. - return baseValue.equals(candidateValue); + return Core.containsConst(t, value); } return false; } @@ -4174,102 +2002,34 @@ boolean isCharLiteralValue(String literal) { * Method to retrieve a type representing all the values in the value space of a finite type that are assignable to * the target type. * - * @param finiteType the finite type - * @param targetType the target type + * @param finiteType finite type + * @param targetType target type * @return a new finite type if at least one value in the value space of the specified finiteType is * assignable to targetType (the same if all are assignable), else semanticError */ - BType getTypeForFiniteTypeValuesAssignableToType(BFiniteType finiteType, BType targetType) { - // finiteType - type Foo "foo"; - // targetType - type FooBar "foo"|"bar"; - if (isAssignable(finiteType, targetType)) { - return finiteType; - } - - // Identify all the values from the value space of the finite type that are assignable to the target type. - // e.g., finiteType - type Foo "foo"|1 ; - Set matchingValues = new HashSet<>(); - for (BLangExpression expr : finiteType.getValueSpace()) { - // case I: targetType - string ("foo" is assignable to string) - BLangLiteral literal = (BLangLiteral) expr; - if (isAssignable(expr.getBType(), targetType) || - // case II: targetType - type Bar "foo"|"baz" ; ("foo" is assignable to Bar) - isAssignableToFiniteType(targetType, literal) || - // type FooVal "foo"; - // case III: targetType - boolean|FooVal ("foo" is assignable to FooVal) - isAssignableToFiniteTypeMemberInUnion(literal, targetType) || - // case IV: targetType - int:Signed16 (1 is assignable to int:Signed16) - isAssignableToBuiltinSubtypeInTargetType(literal, targetType)) { - matchingValues.add(expr); - } - } - - if (matchingValues.isEmpty()) { - return symTable.semanticError; - } - - // Create a new finite type representing the assignable values. - BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, finiteType.tsymbol.flags, - Names.fromString("$anonType$" + UNDERSCORE + finiteTypeCount++), - finiteType.tsymbol.pkgID, null, - finiteType.tsymbol.owner, finiteType.tsymbol.pos, - VIRTUAL); - BFiniteType intersectingFiniteType = new BFiniteType(finiteTypeSymbol, matchingValues); - finiteTypeSymbol.type = intersectingFiniteType; - return intersectingFiniteType; - } - - private boolean isAssignableToFiniteTypeMemberInUnion(BLangLiteral expr, BType targetType) { - targetType = getImpliedType(targetType); - if (targetType.tag != TypeTags.UNION) { - return false; - } - - for (BType memType : ((BUnionType) targetType).getMemberTypes()) { - if (isAssignableToFiniteType(memType, expr)) { - return true; - } - } - return false; - } + private Optional getFiniteTypeForAssignableValues(BType finiteType, BType targetType) { + BFiniteType bFiniteType = (BFiniteType) finiteType; + List newValueSpace = new ArrayList<>(bFiniteType.valueSpace.length); - private boolean isAssignableToBuiltinSubtypeInTargetType(BLangLiteral literal, BType targetType) { - targetType = getImpliedType(targetType); - if (targetType.tag == TypeTags.UNION) { - for (BType memberType : ((BUnionType) targetType).getMemberTypes()) { - if (isLiteralCompatibleWithBuiltinTypeWithSubTypes(literal, memberType)) { - return true; - } + for (SemNamedType semNamedType : bFiniteType.valueSpace) { + if (SemTypes.isSubtype(semTypeCtx, semNamedType.semType(), targetType.semType())) { + newValueSpace.add(semNamedType); } - } - - return isLiteralCompatibleWithBuiltinTypeWithSubTypes(literal, targetType); - } - - public boolean isLiteralCompatibleWithBuiltinTypeWithSubTypes(BLangLiteral literal, BType targetType) { - BType literalType = literal.getBType(); - targetType = getImpliedType(targetType); - if (literalType.tag == targetType.tag) { - return true; - } - - return switch (targetType.tag) { - case TypeTags.BYTE -> literalType.tag == TypeTags.INT && isByteLiteralValue((Long) literal.value); - case TypeTags.SIGNED32_INT -> - literalType.tag == TypeTags.INT && isSigned32LiteralValue((Long) literal.value); - case TypeTags.SIGNED16_INT -> - literalType.tag == TypeTags.INT && isSigned16LiteralValue((Long) literal.value); - case TypeTags.SIGNED8_INT -> literalType.tag == TypeTags.INT && isSigned8LiteralValue((Long) literal.value); - case TypeTags.UNSIGNED32_INT -> - literalType.tag == TypeTags.INT && isUnsigned32LiteralValue((Long) literal.value); - case TypeTags.UNSIGNED16_INT -> - literalType.tag == TypeTags.INT && isUnsigned16LiteralValue((Long) literal.value); - case TypeTags.UNSIGNED8_INT -> - literalType.tag == TypeTags.INT && isUnsigned8LiteralValue((Long) literal.value); - case TypeTags.CHAR_STRING -> - literalType.tag == TypeTags.STRING && isCharLiteralValue((String) literal.value); - default -> false; - }; + } + + if (newValueSpace.isEmpty()) { + return Optional.empty(); + } + + // Create a new finite type representing the assignable values. + BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, finiteType.tsymbol.flags, + Names.fromString("$anonType$" + UNDERSCORE + finiteTypeCount++), + finiteType.tsymbol.pkgID, null, + finiteType.tsymbol.owner, finiteType.tsymbol.pos, + VIRTUAL); + BFiniteType ft = new BFiniteType(finiteTypeSymbol, newValueSpace.toArray(SemNamedType[]::new)); + finiteTypeSymbol.type = ft; + return Optional.of(ft); } /** @@ -4306,201 +2066,71 @@ BType getTypeForUnionTypeMembersAssignableToType(BUnionType unionType, BType tar if (intersection.size() == 1) { return intersection.get(0); } else { - return BUnionType.create(null, new LinkedHashSet<>(intersection)); + return BUnionType.create(typeEnv(), null, new LinkedHashSet<>(intersection)); } } boolean validEqualityIntersectionExists(BType lhsType, BType rhsType) { - if (!isAnydata(lhsType) && !isAnydata(rhsType)) { + SemType intersect = Core.intersect(lhsType.semType(), rhsType.semType()); + if (Core.isEmpty(semTypeCtx, intersect)) { return false; } - if (isAssignable(lhsType, rhsType) || isAssignable(rhsType, lhsType)) { - return true; - } - - Set lhsTypes = expandAndGetMemberTypesRecursive(lhsType); - Set rhsTypes = expandAndGetMemberTypesRecursive(rhsType); - return equalityIntersectionExists(lhsTypes, rhsTypes); + return isAnydata(intersect); } - private boolean equalityIntersectionExists(Set lhsTypes, Set rhsTypes) { - if ((lhsTypes.contains(symTable.anydataType) && - rhsTypes.stream().anyMatch(type -> getImpliedType(type).tag != TypeTags.ERROR)) || - (rhsTypes.contains(symTable.anydataType) && - lhsTypes.stream().anyMatch(type -> getImpliedType(type).tag != TypeTags.ERROR))) { - return true; - } - - boolean matchFound = false; - for (BType lhsType : lhsTypes) { - for (BType rhsType : rhsTypes) { - if (isAssignable(lhsType, rhsType) || isAssignable(rhsType, lhsType)) { - matchFound = true; - break; - } - } - if (matchFound) { - break; - } - } - - if (!matchFound) { - matchFound = equalityIntersectionExistsForComplexTypes(lhsTypes, rhsTypes); - } - - return matchFound; + /** + * Checks where a type is subtype of either string or xml. + * + * @param type type to be checked + * @return a boolean + */ + boolean validStringOrXmlTypeExists(BType type) { + return isStringSubtype(type) || isXmlSubType(type); } - boolean validNumericStringOrXmlTypeExists(BType type, TypeExistenceValidationFunction validationFunction) { - switch (type.tag) { - case TypeTags.UNION: - BUnionType unionType = (BUnionType) type; - Set memberTypes = unionType.getMemberTypes(); - BType firstTypeInUnion = getBasicTypeOfBuiltinSubtype(getImpliedType(memberTypes.iterator().next())); - if (!validationFunction.validate(firstTypeInUnion)) { - return false; - } - if (firstTypeInUnion.tag == TypeTags.FINITE) { - Set valSpace = ((BFiniteType) firstTypeInUnion).getValueSpace(); - BType baseExprType = valSpace.iterator().next().getBType(); - for (BType memType : memberTypes) { - memType = getImpliedType(memType); - if (memType.tag == TypeTags.FINITE) { - if (!checkValueSpaceHasSameType((BFiniteType) memType, baseExprType)) { - return false; - } - continue; - } - - if (!isSubTypeOfBaseType(memType, baseExprType.tag)) { - return false; - } - } - } else { - for (BType memType : memberTypes) { - memType = getImpliedType(memType); - if (memType.tag == TypeTags.FINITE) { - if (!checkValueSpaceHasSameType((BFiniteType) memType, firstTypeInUnion)) { - return false; - } - continue; - } - if (!isSubTypeOfBaseType(memType, firstTypeInUnion.tag)) { - return false; - } - } - } - return true; - case TypeTags.FINITE: - Set valSpace = ((BFiniteType) type).getValueSpace(); - BType baseExprType = valSpace.iterator().next().getBType(); - for (BLangExpression expr : valSpace) { - if (!checkValueSpaceHasSameType((BFiniteType) type, baseExprType)) { - return false; - } - if (!validationFunction.validate(expr.getBType())) { - return false; - } - } - return true; - case TypeTags.TYPEREFDESC: - return validationFunction.validate(getImpliedType(type)); - case TypeTags.INTERSECTION: - return validationFunction.validate(((BIntersectionType) type).effectiveType); - default: - return false; - } + /** + * Checks whether a type is a subtype of xml. + * + * @param type type to be checked + * @return a boolean + */ + boolean isXmlSubType(BType type) { + return SemTypeHelper.isSubtypeSimple(type, PredefinedType.XML); } - boolean validNumericTypeExists(BType type) { - if (type.isNullable() && getImpliedType(type).tag != TypeTags.NIL) { - type = getSafeType(type, true, false); - } - if (isBasicNumericType(type)) { - return true; - } - return validNumericStringOrXmlTypeExists(type, this::validNumericTypeExists); + /** + * Checks whether a type is a subtype of string. + * + * @param type type to be checked + * @return a boolean + */ + boolean isStringSubtype(BType type) { + return SemTypeHelper.isSubtypeSimple(type, PredefinedType.STRING); } - boolean validStringOrXmlTypeExists(BType type) { - BType refType = getImpliedType(type); - if (TypeTags.isStringTypeTag(refType.tag) || TypeTags.isXMLTypeTag(refType.tag)) { - return true; - } - return validNumericStringOrXmlTypeExists(type, this::validStringOrXmlTypeExists); + /** + * Checks whether a type is a subtype of one of int?, float? or decimal?. + * + * @param type type to be checked + * @return a boolean + */ + boolean validNumericTypeExists(BType type) { + SemType tButNil = Core.diff(type.semType(), PredefinedType.NIL); // nil lift + BasicTypeBitSet basicTypeBitSet = Core.widenToBasicTypes(tButNil); + return basicTypeBitSet.equals(PredefinedType.INT) || + basicTypeBitSet.equals(PredefinedType.FLOAT) || + basicTypeBitSet.equals(PredefinedType.DECIMAL); } boolean validIntegerTypeExists(BType bType) { - BType type = getImpliedType(bType); - if (type.isNullable() && type.tag != TypeTags.NIL) { - type = getSafeType(type, true, false); - } - if (TypeTags.isIntegerTypeTag(type.tag)) { - return true; - } - switch (type.tag) { - case TypeTags.BYTE: - return true; - case TypeTags.UNION: - LinkedHashSet memberTypes = ((BUnionType) type).getMemberTypes(); - for (BType memberType : memberTypes) { - memberType = getImpliedType(memberType); - if (!validIntegerTypeExists(memberType)) { - return false; - } - } - return true; - case TypeTags.FINITE: - Set valueSpace = ((BFiniteType) type).getValueSpace(); - for (BLangExpression expr : valueSpace) { - if (!validIntegerTypeExists(expr.getBType())) { - return false; - } - } - return true; - default: - return false; - } - } - - public BType getBasicTypeOfBuiltinSubtype(BType type) { - if (TypeTags.isIntegerTypeTag(type.tag) || type.tag == TypeTags.BYTE) { - return symTable.intType; - } - if (TypeTags.isStringTypeTag(type.tag)) { - return symTable.stringType; - } - if (TypeTags.isXMLTypeTag(type.tag)) { - return symTable.xmlType; - } - return type; + SemType s = bType.semType(); + s = Core.diff(s, PredefinedType.NIL); // nil lift + return SemTypes.isSubtypeSimpleNotNever(s, PredefinedType.INT); } - public boolean checkTypeContainString(BType type) { - type = getImpliedType(type); - if (TypeTags.isStringTypeTag(type.tag)) { - return true; - } - switch (type.tag) { - case TypeTags.UNION: - for (BType memType : ((BUnionType) type).getMemberTypes()) { - if (!checkTypeContainString(memType)) { - return false; - } - } - return true; - case TypeTags.FINITE: - Set valSpace = ((BFiniteType) type).getValueSpace(); - for (BLangExpression expr : valSpace) { - if (!checkTypeContainString(expr.getBType())) { - return false; - } - } - return true; - default: - return false; - } + public boolean isStringSubType(BType type) { + return SemTypeHelper.isSubtypeSimpleNotNever(type, PredefinedType.STRING); } /** @@ -4528,8 +2158,8 @@ private Set expandAndGetMemberTypesRecursiveHelper(BType bType, memberTypes.add(symTable.byteType); break; case TypeTags.FINITE: - BFiniteType expType = (BFiniteType) referredType; - expType.getValueSpace().forEach(value -> memberTypes.add(value.getBType())); + Set broadTypes = SemTypeHelper.broadTypes((BFiniteType) referredType, symTable); + memberTypes.addAll(broadTypes); break; case TypeTags.UNION: BUnionType unionType = (BUnionType) referredType; @@ -4546,12 +2176,13 @@ private Set expandAndGetMemberTypesRecursiveHelper(BType bType, // add an unsealed array to allow comparison between closed and open arrays // TODO: 10/16/18 improve this, since it will allow comparison between sealed arrays of different sizes if (((BArrayType) referredType).getSize() != -1) { - memberTypes.add(new BArrayType(arrayElementType)); + memberTypes.add(new BArrayType(typeEnv(), arrayElementType)); } if (getImpliedType(arrayElementType).tag == TypeTags.UNION) { Set elementUnionTypes = expandAndGetMemberTypesRecursiveHelper(arrayElementType, visited); - elementUnionTypes.forEach(elementUnionType -> memberTypes.add(new BArrayType(elementUnionType))); + elementUnionTypes.forEach( + elementUnionType -> memberTypes.add(new BArrayType(typeEnv(), elementUnionType))); } memberTypes.add(bType); break; @@ -4560,8 +2191,9 @@ private Set expandAndGetMemberTypesRecursiveHelper(BType bType, if (getImpliedType(mapConstraintType).tag == TypeTags.UNION) { Set constraintUnionTypes = expandAndGetMemberTypesRecursiveHelper(mapConstraintType, visited); - constraintUnionTypes.forEach(constraintUnionType -> - memberTypes.add(new BMapType(TypeTags.MAP, constraintUnionType, symTable.mapType.tsymbol))); + constraintUnionTypes.forEach(constraintUnionType -> memberTypes.add( + new BMapType(symTable.typeEnv(), TypeTags.MAP, constraintUnionType, + symTable.mapType.tsymbol))); } memberTypes.add(bType); break; @@ -4571,235 +2203,6 @@ private Set expandAndGetMemberTypesRecursiveHelper(BType bType, return memberTypes; } - private boolean tupleIntersectionExists(BTupleType lhsType, BTupleType rhsType) { - if (lhsType.getTupleTypes().size() != rhsType.getTupleTypes().size()) { - return false; - } - - List lhsMemberTypes = lhsType.getTupleTypes(); - List rhsMemberTypes = rhsType.getTupleTypes(); - - for (int i = 0; i < lhsType.getTupleTypes().size(); i++) { - if (!equalityIntersectionExists(expandAndGetMemberTypesRecursive(lhsMemberTypes.get(i)), - expandAndGetMemberTypesRecursive(rhsMemberTypes.get(i)))) { - return false; - } - } - return true; - } - - private boolean equalityIntersectionExistsForComplexTypes(Set lhsTypes, Set rhsTypes) { - for (BType lhsMemberType : lhsTypes) { - if (isEqualityIntersectionExistsForMemberType(lhsMemberType, rhsTypes)) { - return true; - } - } - return false; - } - - private boolean isEqualityIntersectionExistsForMemberType(BType lhsMemberType, Set rhsTypes) { - switch (lhsMemberType.tag) { - case TypeTags.INT: - case TypeTags.STRING: - case TypeTags.FLOAT: - case TypeTags.DECIMAL: - case TypeTags.BOOLEAN: - case TypeTags.NIL: - if (rhsTypes.stream().map(Types::getImpliedType) - .anyMatch(rhsMemberType -> rhsMemberType.tag == TypeTags.JSON)) { - return true; - } - break; - case TypeTags.JSON: - if (jsonEqualityIntersectionExists(rhsTypes)) { - return true; - } - break; - // When expanding members for tuples, arrays and maps, set isValueDeepEquality to true, to allow - // comparison between JSON lists/maps and primitive lists/maps since they are all reference types - case TypeTags.TUPLE: - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.TUPLE && - tupleIntersectionExists((BTupleType) lhsMemberType, (BTupleType) rhsMemberType))) { - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.ARRAY && - arrayTupleEqualityIntersectionExists((BArrayType) rhsMemberType, - (BTupleType) lhsMemberType))) { - return true; - } - break; - case TypeTags.ARRAY: - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.ARRAY && - equalityIntersectionExists( - expandAndGetMemberTypesRecursive(((BArrayType) lhsMemberType).eType), - expandAndGetMemberTypesRecursive(((BArrayType) rhsMemberType).eType)))) { - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.TUPLE && - arrayTupleEqualityIntersectionExists((BArrayType) lhsMemberType, - (BTupleType) rhsMemberType))) { - return true; - } - break; - case TypeTags.MAP: - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.MAP && - equalityIntersectionExists( - expandAndGetMemberTypesRecursive(((BMapType) lhsMemberType).constraint), - expandAndGetMemberTypesRecursive(((BMapType) rhsMemberType).constraint)))) { - return true; - } - - if (!isAssignable(((BMapType) lhsMemberType).constraint, symTable.errorType) && - rhsTypes.stream().map(Types::getImpliedType).anyMatch(rhsMemberType - -> rhsMemberType.tag == TypeTags.JSON)) { - // at this point it is guaranteed that the map is anydata - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.RECORD && - mapRecordEqualityIntersectionExists((BMapType) lhsMemberType, - (BRecordType) rhsMemberType))) { - return true; - } - break; - case TypeTags.OBJECT: - case TypeTags.RECORD: - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> checkStructEquivalency(rhsMemberType, lhsMemberType) || - checkStructEquivalency(lhsMemberType, rhsMemberType))) { - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.RECORD && - recordEqualityIntersectionExists((BRecordType) lhsMemberType, - (BRecordType) rhsMemberType))) { - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch(rhsMemberType - -> rhsMemberType.tag == TypeTags.JSON) && - jsonEqualityIntersectionExists(expandAndGetMemberTypesRecursive(lhsMemberType))) { - return true; - } - - if (rhsTypes.stream().map(Types::getImpliedType).anyMatch( - rhsMemberType -> rhsMemberType.tag == TypeTags.MAP && - mapRecordEqualityIntersectionExists((BMapType) rhsMemberType, - (BRecordType) lhsMemberType))) { - return true; - } - break; - case TypeTags.TYPEREFDESC: - case TypeTags.INTERSECTION: - return isEqualityIntersectionExistsForMemberType(getImpliedType(lhsMemberType), rhsTypes); - } - return false; - } - - private boolean arrayTupleEqualityIntersectionExists(BArrayType arrayType, BTupleType tupleType) { - Set elementTypes = expandAndGetMemberTypesRecursive(arrayType.eType); - - return tupleType.getTupleTypes().stream().allMatch(tupleMemType -> - equalityIntersectionExists(elementTypes, expandAndGetMemberTypesRecursive(tupleMemType))); - } - - private boolean recordEqualityIntersectionExists(BRecordType lhsType, BRecordType rhsType) { - Map lhsFields = lhsType.fields; - Map rhsFields = rhsType.fields; - - List matchedFieldNames = new ArrayList<>(); - for (BField lhsField : lhsFields.values()) { - if (rhsFields.containsKey(lhsField.name.value)) { - if (!equalityIntersectionExists(expandAndGetMemberTypesRecursive(lhsField.type), - expandAndGetMemberTypesRecursive( - rhsFields.get(lhsField.name.value).type))) { - return false; - } - matchedFieldNames.add(lhsField.getName()); - } else { - if (Symbols.isFlagOn(lhsField.symbol.flags, Flags.OPTIONAL)) { - break; - } - - if (rhsType.sealed) { - return false; - } - - if (!equalityIntersectionExists(expandAndGetMemberTypesRecursive(lhsField.type), - expandAndGetMemberTypesRecursive(rhsType.restFieldType))) { - return false; - } - } - } - - for (BField rhsField : rhsFields.values()) { - if (matchedFieldNames.contains(rhsField.getName())) { - continue; - } - - if (!Symbols.isFlagOn(rhsField.symbol.flags, Flags.OPTIONAL)) { - if (lhsType.sealed) { - return false; - } - - if (!equalityIntersectionExists(expandAndGetMemberTypesRecursive(rhsField.type), - expandAndGetMemberTypesRecursive(lhsType.restFieldType))) { - return false; - } - } - } - - return true; - } - - private boolean mapRecordEqualityIntersectionExists(BMapType mapType, BRecordType recordType) { - Set mapConstrTypes = expandAndGetMemberTypesRecursive(mapType.getConstraint()); - - for (BField field : recordType.fields.values()) { - if (!Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL) && - !equalityIntersectionExists(mapConstrTypes, expandAndGetMemberTypesRecursive(field.type))) { - return false; - } - } - - return true; - } - - private boolean jsonEqualityIntersectionExists(Set typeSet) { - for (BType type : typeSet) { - type = getImpliedType(type); - switch (type.tag) { - case TypeTags.MAP: - if (!isAssignable(((BMapType) type).constraint, symTable.errorType)) { - return true; - } - break; - case TypeTags.RECORD: - BRecordType recordType = (BRecordType) type; - if (recordType.fields.values().stream() - .allMatch(field -> Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL) || - !isAssignable(field.type, symTable.errorType))) { - return true; - } - break; - default: - if (isAssignable(type, symTable.jsonType)) { - return true; - } - } - } - return false; - } - public BType getRemainingMatchExprType(BType originalType, BType typeToRemove, SymbolEnv env) { originalType = getImpliedType(originalType); return switch (originalType.tag) { @@ -4832,11 +2235,11 @@ private BType getRemainingType(BTupleType originalType, BTupleType typeToRemove, List tupleTypes = new ArrayList<>(); for (int i = 0; i < originalTupleTypes.size(); i++) { BType type = getRemainingMatchExprType(originalTupleTypes.get(i), typesToRemove.get(i), env); - BVarSymbol varSymbol = new BVarSymbol(type.flags, null, null, type, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(type.getFlags(), null, null, type, null, null, null); tupleTypes.add(new BTupleMember(type, varSymbol)); } if (typeToRemove.restType == null) { - return new BTupleType(tupleTypes); + return new BTupleType(typeEnv(), tupleTypes); } if (originalTupleTypes.size() == typesToRemove.size()) { return originalType; @@ -4846,7 +2249,7 @@ private BType getRemainingType(BTupleType originalType, BTupleType typeToRemove, BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); tupleTypes.add(new BTupleMember(type, varSymbol)); } - return new BTupleType(tupleTypes); + return new BTupleType(typeEnv(), tupleTypes); } private BType getRemainingType(BTupleType originalType, BArrayType typeToRemove, SymbolEnv env) { @@ -4857,7 +2260,7 @@ private BType getRemainingType(BTupleType originalType, BArrayType typeToRemove, BVarSymbol varSymbol = Symbols.createVarSymbolForTupleMember(type); tupleTypes.add(new BTupleMember(type, varSymbol)); } - BTupleType remainingType = new BTupleType(tupleTypes); + BTupleType remainingType = new BTupleType(typeEnv(), tupleTypes); if (originalType.restType != null) { remainingType.restType = getRemainingMatchExprType(originalType.restType, eType, env); } @@ -4882,8 +2285,8 @@ public BType getRemainingType(BType originalType, BType typeToRemove, SymbolEnv getAllTypes(remainingType, true))); if (typeRemovedFromOriginalUnionType == symTable.nullSet || - isSubTypeOfReadOnly(typeRemovedFromOriginalUnionType, env) || - isSubTypeOfReadOnly(remainingType, env) || + isSubTypeOfReadOnly(typeRemovedFromOriginalUnionType) || + isSubTypeOfReadOnly(remainingType) || narrowsToUnionOfImmutableTypesOrDistinctBasicTypes(remainingType, typeToRemove, env)) { return remainingType; } @@ -4902,7 +2305,7 @@ public BType getRemainingType(BType originalType, BType typeToRemove, SymbolEnv return getRemainingType(refType, typeToRemove, env); } - if (Symbols.isFlagOn(getImpliedType(originalType).flags, Flags.READONLY)) { + if (Symbols.isFlagOn(getImpliedType(originalType).getFlags(), Flags.READONLY)) { return remainingType; } @@ -4928,10 +2331,12 @@ public BType getRemainingType(BType originalType, BType typeToRemove, SymbolEnv return originalType; } - public boolean isSubTypeOfReadOnly(BType type, SymbolEnv env) { - return isInherentlyImmutableType(type) || - (isSelectivelyImmutableType(type, env.enclPkg.packageID) && - Symbols.isFlagOn(type.flags, Flags.READONLY)); + public boolean isSubTypeOfReadOnly(SemType t) { + return isSubtype(t, PredefinedType.VAL_READONLY); + } + + public boolean isSubTypeOfReadOnly(BType type) { + return isSubTypeOfReadOnly(type.semType()); } private boolean isClosedRecordTypes(BType type) { @@ -5050,7 +2455,7 @@ private boolean narrowsToUnionOfImmutableTypesOrDistinctBasicTypes(BType remaini LinkedHashSet mutableRemainingTypes = filterMutableMembers(((BUnionType) referredRemainingType).getMemberTypes(), env); remainingType = mutableRemainingTypes.size() == 1 ? mutableRemainingTypes.iterator().next() : - BUnionType.create(null, mutableRemainingTypes); + BUnionType.create(typeEnv(), null, mutableRemainingTypes); BType referredTypeToRemove = getImpliedType(typeToRemove); @@ -5058,7 +2463,7 @@ private boolean narrowsToUnionOfImmutableTypesOrDistinctBasicTypes(BType remaini LinkedHashSet mutableTypesToRemove = filterMutableMembers(((BUnionType) referredTypeToRemove).getMemberTypes(), env); typeToRemove = mutableTypesToRemove.size() == 1 ? mutableTypesToRemove.iterator().next() : - BUnionType.create(null, mutableTypesToRemove); + BUnionType.create(typeEnv(), null, mutableTypesToRemove); } else { typeToRemove = referredTypeToRemove; } @@ -5071,7 +2476,7 @@ private LinkedHashSet filterMutableMembers(LinkedHashSet types, Sy for (BType type : types) { BType referredType = getImpliedType(type); - if (!isSubTypeOfReadOnly(referredType, env)) { + if (!isSubTypeOfReadOnly(referredType)) { remainingMemberTypes.add(referredType); } } @@ -5088,6 +2493,10 @@ private BType getRemainingType(BReadonlyType originalType, BType removeType) { return originalType; } + public boolean intersectionExists(SemType t1, SemType t2) { + return !Core.isEmpty(semTypeCtx, Core.intersect(t1, t2)); + } + public BType getTypeIntersection(IntersectionContext intersectionContext, BType lhsType, BType rhsType, SymbolEnv env) { return getTypeIntersection(intersectionContext, lhsType, rhsType, env, new LinkedHashSet<>()); @@ -5116,7 +2525,7 @@ private BType getTypeIntersection(IntersectionContext intersectionContext, BType if (intersection.size() == 1) { return intersection.toArray(new BType[0])[0]; } else { - return BUnionType.create(null, intersection); + return BUnionType.create(typeEnv(), null, intersection); } } @@ -5139,8 +2548,8 @@ private BType getIntersection(IntersectionContext intersectionContext, BType lhs // implementation, we cannot easily find the intersection between (A & readonly) and B. Instead, what we // do here is, first find the intersection between A and B then re-create the immutable type out of it. - if (Symbols.isFlagOn(referredLhsType.flags, Flags.READONLY) && referredLhsType.tag == TypeTags.INTERSECTION && - getImpliedType(((BIntersectionType) referredLhsType).effectiveType).tag == TypeTags.UNION) { + if (Symbols.isFlagOn(referredLhsType.getFlags(), Flags.READONLY) && referredLhsType.tag == TypeTags.INTERSECTION + && getImpliedType(((BIntersectionType) referredLhsType).effectiveType).tag == TypeTags.UNION) { BIntersectionType intersectionType = (BIntersectionType) referredLhsType; BType finalType = type; List types = intersectionType.getConstituentTypes().stream() @@ -5151,7 +2560,7 @@ private BType getIntersection(IntersectionContext intersectionContext, BType lhs if (types.size() == 1) { BType bType = types.get(0); - if (isInherentlyImmutableType(bType) || Symbols.isFlagOn(bType.flags, Flags.READONLY)) { + if (isInherentlyImmutableType(bType) || Symbols.isFlagOn(bType.getFlags(), Flags.READONLY)) { return bType; } @@ -5197,15 +2606,15 @@ private BType getIntersection(IntersectionContext intersectionContext, BType lhs } else if (!intersectionContext.preferNonGenerativeIntersection && isAssignable(referredLhsType, referredType)) { return lhsType; - } else if (referredLhsType.tag == TypeTags.FINITE) { - BType intersectionType = getTypeForFiniteTypeValuesAssignableToType((BFiniteType) referredLhsType, type); - if (intersectionType != symTable.semanticError) { - return intersectionType; + } else if (referredLhsType.tag == TypeTags.FINITE) { + Optional intersectionType = getFiniteTypeForAssignableValues(referredLhsType, type); + if (intersectionType.isPresent()) { + return intersectionType.get(); } } else if (referredType.tag == TypeTags.FINITE) { - BType intersectionType = getTypeForFiniteTypeValuesAssignableToType((BFiniteType) referredType, lhsType); - if (intersectionType != symTable.semanticError) { - return intersectionType; + Optional intersectionType = getFiniteTypeForAssignableValues(referredType, lhsType); + if (intersectionType.isPresent()) { + return intersectionType.get(); } } else if (referredLhsType.tag == TypeTags.UNION) { BType intersectionType = getTypeForUnionTypeMembersAssignableToType((BUnionType) referredLhsType, type, env, @@ -5225,7 +2634,7 @@ private BType getIntersection(IntersectionContext intersectionContext, BType lhs if (intersectionConstraintTypeType == null || intersectionConstraintTypeType == symTable.semanticError) { return null; } - return new BMapType(TypeTags.MAP, intersectionConstraintTypeType, null); + return new BMapType(symTable.typeEnv(), TypeTags.MAP, intersectionConstraintTypeType, null); } else if (referredType.tag == TypeTags.ARRAY && referredLhsType.tag == TypeTags.TUPLE) { BType intersectionType = createArrayAndTupleIntersection(intersectionContext, (BArrayType) referredType, (BTupleType) referredLhsType, env, visitedTypes); @@ -5281,14 +2690,14 @@ private BType getIntersection(IntersectionContext intersectionContext, BType lhs if (elementIntersection == null) { return null; } - return new BArrayType(elementIntersection); + return new BArrayType(typeEnv(), elementIntersection); } else if (referredType.tag == TypeTags.ARRAY && isAnydataOrJson(referredLhsType)) { BType elementIntersection = getIntersection(intersectionContext, lhsType, env, ((BArrayType) referredType).eType, visitedTypes); if (elementIntersection == null) { return null; } - return new BArrayType(elementIntersection); + return new BArrayType(typeEnv(), elementIntersection); } else if (referredType.tag == TypeTags.NULL_SET) { return type; } @@ -5331,8 +2740,8 @@ private BType createArrayAndTupleIntersection(IntersectionContext intersectionCo return tupleType; } List tupleTypes = tupleType.getTupleTypes(); - if (arrayType.state == BArrayState.CLOSED && tupleTypes.size() != arrayType.size) { - if (tupleTypes.size() > arrayType.size) { + if (arrayType.state == BArrayState.CLOSED && tupleTypes.size() != arrayType.getSize()) { + if (tupleTypes.size() > arrayType.getSize()) { return symTable.semanticError; } @@ -5354,15 +2763,15 @@ private BType createArrayAndTupleIntersection(IntersectionContext intersectionCo } if (tupleType.restType == null) { - return new BTupleType(null, tupleMemberTypes); + return new BTupleType(typeEnv(), tupleMemberTypes); } BType restIntersectionType = getTypeIntersection(intersectionContext, tupleType.restType, eType, env, visitedTypes); if (restIntersectionType == symTable.semanticError) { - return new BTupleType(null, tupleMemberTypes); + return new BTupleType(typeEnv(), tupleMemberTypes); } - return new BTupleType(null, tupleMemberTypes, restIntersectionType, 0); + return new BTupleType(typeEnv(), null, tupleMemberTypes, restIntersectionType, 0); } private BType createTupleAndTupleIntersection(IntersectionContext intersectionContext, @@ -5392,7 +2801,7 @@ private BType createTupleAndTupleIntersection(IntersectionContext intersectionCo if (intersectionType == symTable.semanticError) { return symTable.semanticError; } - BVarSymbol varSymbol = new BVarSymbol(intersectionType.flags, null, null, intersectionType, + BVarSymbol varSymbol = new BVarSymbol(intersectionType.getFlags(), null, null, intersectionType, null, null, null); tupleMemberTypes.add(new BTupleMember(intersectionType, varSymbol)); } @@ -5401,12 +2810,12 @@ private BType createTupleAndTupleIntersection(IntersectionContext intersectionCo BType restIntersectionType = getTypeIntersection(intersectionContext, tupleType.restType, lhsTupleType.restType, env, visitedTypes); if (restIntersectionType == symTable.semanticError) { - return new BTupleType(null, tupleMemberTypes); + return new BTupleType(typeEnv(), tupleMemberTypes); } - return new BTupleType(null, tupleMemberTypes, restIntersectionType, 0); + return new BTupleType(typeEnv(), null, tupleMemberTypes, restIntersectionType, 0); } - return new BTupleType(null, tupleMemberTypes); + return new BTupleType(typeEnv(), tupleMemberTypes); } private BType getIntersectionForErrorTypes(IntersectionContext intersectionContext, @@ -5477,7 +2886,7 @@ private BType createRecordIntersection(IntersectionContext intersectionContext, if ((newType.sealed || newType.restFieldType == symTable.neverType) && (newTypeFields.isEmpty() || allReadOnlyFields(newTypeFields))) { - newType.flags |= Flags.READONLY; + newType.addFlags(Flags.READONLY); newTypeSymbol.flags |= Flags.READONLY; } @@ -5630,14 +3039,14 @@ private BRecordType createAnonymousRecord(SymbolEnv env) { env.scope.owner, null, VIRTUAL); recordSymbol.name = Names.fromString( anonymousModelHelper.getNextAnonymousTypeKey(env.enclPkg.packageID)); - BInvokableType bInvokableType = new BInvokableType(new ArrayList<>(), symTable.nilType, null); + BInvokableType bInvokableType = new BInvokableType(typeEnv(), List.of(), symTable.nilType, null); BInvokableSymbol initFuncSymbol = Symbols.createFunctionSymbol( Flags.PUBLIC, Names.EMPTY, Names.EMPTY, env.enclPkg.symbol.pkgID, bInvokableType, env.scope.owner, false, symTable.builtinPos, VIRTUAL); initFuncSymbol.retType = symTable.nilType; recordSymbol.scope = new Scope(recordSymbol); - BRecordType recordType = new BRecordType(recordSymbol); + BRecordType recordType = new BRecordType(symTable.typeEnv(), recordSymbol); recordType.tsymbol = recordSymbol; recordSymbol.type = recordType; @@ -5645,7 +3054,7 @@ private BRecordType createAnonymousRecord(SymbolEnv env) { } private BRecordType getEquivalentRecordType(BMapType mapType) { - BRecordType equivalentRecordType = new BRecordType(null); + BRecordType equivalentRecordType = new BRecordType(symTable.typeEnv(), null); equivalentRecordType.sealed = false; equivalentRecordType.restFieldType = mapType.constraint; return equivalentRecordType; @@ -5655,11 +3064,19 @@ private BErrorType createErrorType(BType lhsType, BType rhsType, BType detailTyp BErrorType lhsErrorType = (BErrorType) lhsType; BErrorType rhsErrorType = (BErrorType) rhsType; - long flags = lhsType.flags | rhsType.flags | Flags.PUBLIC; // Anonymous (generated) types are marked as public. - BErrorType errorType = createErrorType(detailType, flags, env); + // Anonymous (generated) types are marked as public. + BErrorType errorType = createErrorType(detailType, lhsType.getFlags() | rhsType.getFlags() | Flags.PUBLIC, env); - errorType.typeIdSet = BTypeIdSet.getIntersection(lhsErrorType.typeIdSet, rhsErrorType.typeIdSet); + // This is to propagate same distinctId to effective type + lhsErrorType.setDistinctId(); + rhsErrorType.setDistinctId(); + if (lhsErrorType.distinctId != -1) { + errorType.distinctId = lhsErrorType.distinctId; + } else if (rhsErrorType.distinctId != -1) { + errorType.distinctId = rhsErrorType.distinctId; + } + errorType.typeIdSet = BTypeIdSet.getIntersection(lhsErrorType.typeIdSet, rhsErrorType.typeIdSet); return errorType; } @@ -5669,62 +3086,14 @@ public BErrorType createErrorType(BType detailType, long flags, SymbolEnv env) { env.enclPkg.symbol.pkgID, null, env.scope.owner, symTable.builtinPos, VIRTUAL); errorTypeSymbol.scope = new Scope(errorTypeSymbol); - BErrorType errorType = new BErrorType(errorTypeSymbol, detailType); - errorType.flags |= errorTypeSymbol.flags; + BErrorType errorType = new BErrorType(symTable.typeEnv(), errorTypeSymbol, detailType); + errorType.addFlags(errorTypeSymbol.flags); errorTypeSymbol.type = errorType; errorType.typeIdSet = BTypeIdSet.emptySet(); return errorType; } - private boolean populateRecordFields(IntersectionContext diagnosticContext, BRecordType newType, - BType originalType, SymbolEnv env, BType constraint) { - BTypeSymbol intersectionRecordSymbol = newType.tsymbol; - // If the detail type is BMapType simply ignore since the resulting detail type has `anydata` as rest type. - if (originalType.getKind() != TypeKind.RECORD) { - return true; - } - BRecordType originalRecordType = (BRecordType) originalType; - LinkedHashMap fields = new LinkedHashMap<>(); - for (BField origField : originalRecordType.fields.values()) { - org.wso2.ballerinalang.compiler.util.Name origFieldName = origField.name; - String nameString = origFieldName.value; - - if (!validateRecordFieldDefaultValueForIntersection(diagnosticContext, origField, originalRecordType)) { - return false; - } - - BType recordFieldType = validateRecordField(diagnosticContext, newType, origField, constraint, env); - if (recordFieldType == symTable.semanticError) { - return false; - } - - BVarSymbol recordFieldSymbol = new BVarSymbol(origField.symbol.flags, origFieldName, - env.enclPkg.packageID, recordFieldType, - intersectionRecordSymbol, origField.pos, SOURCE); - - if (recordFieldType == symTable.neverType && Symbols.isFlagOn(recordFieldSymbol.flags, Flags.OPTIONAL)) { - recordFieldSymbol.flags &= (~Flags.REQUIRED); - recordFieldSymbol.flags |= Flags.OPTIONAL; - } - - if (getImpliedType(recordFieldType).tag == TypeTags.INVOKABLE && recordFieldType.tsymbol != null) { - BInvokableTypeSymbol tsymbol = (BInvokableTypeSymbol) recordFieldType.tsymbol; - BInvokableSymbol invokableSymbol = (BInvokableSymbol) recordFieldSymbol; - invokableSymbol.params = tsymbol.params == null ? null : new ArrayList<>(tsymbol.params); - invokableSymbol.restParam = tsymbol.restParam; - invokableSymbol.retType = tsymbol.returnType; - invokableSymbol.flags = tsymbol.flags; - } - - fields.put(nameString, new BField(origFieldName, null, recordFieldSymbol)); - intersectionRecordSymbol.scope.define(origFieldName, recordFieldSymbol); - } - newType.fields.putAll(fields); - - return true; - } - private boolean validateRecordFieldDefaultValueForIntersection(IntersectionContext diagnosticContext, BField field, BRecordType recordType) { @@ -5735,52 +3104,6 @@ private boolean validateRecordFieldDefaultValueForIntersection(IntersectionConte return true; } - private BType validateRecordField(IntersectionContext intersectionContext, - BRecordType newType, BField origField, BType constraint, SymbolEnv env) { - if (hasField(newType, origField)) { - return validateOverlappingFields(newType, origField); - } - - if (constraint == null) { - return origField.type; - } - - BType fieldType = getTypeIntersection(intersectionContext, origField.type, constraint, env); - if (fieldType.tag == TypeTags.NEVER && !Symbols.isOptional(origField.symbol)) { - return symTable.semanticError; - } - - if (fieldType != symTable.semanticError) { - return fieldType; - } - - if (Symbols.isOptional(origField.symbol)) { - return symTable.neverType; - } - - return symTable.semanticError; - } - - private boolean hasField(BRecordType recordType, BField origField) { - return recordType.fields.containsKey(origField.name.value); - } - - private BType validateOverlappingFields(BRecordType newType, BField origField) { - if (!hasField(newType, origField)) { - return origField.type; - } - - BField overlappingField = newType.fields.get(origField.name.value); - if (isAssignable(overlappingField.type, origField.type)) { - return overlappingField.type; - } - - if (isAssignable(origField.type, overlappingField.type)) { - return origField.type; - } - return symTable.semanticError; - } - private void removeErrorFromReadonlyType(List remainingTypes) { Iterator remainingIterator = remainingTypes.listIterator(); boolean addAnyAndReadOnly = false; @@ -5834,28 +3157,23 @@ private BType getRemainingType(BUnionType originalType, List removeTypes) return symTable.nullSet; } - return BUnionType.create(null, new LinkedHashSet<>(remainingTypes)); + return BUnionType.create(typeEnv(), null, new LinkedHashSet<>(remainingTypes)); } private BType getRemainingType(BFiniteType originalType, List removeTypes) { - Set remainingValueSpace = new LinkedHashSet<>(); - - for (BLangExpression valueExpr : originalType.getValueSpace()) { - boolean matchExists = false; - for (BType remType : removeTypes) { - if (isAssignable(valueExpr.getBType(), remType) || - isAssignableToFiniteType(remType, (BLangLiteral) valueExpr)) { - matchExists = true; - break; - } - } + SemType removeSemType = PredefinedType.NEVER; + for (BType removeType : removeTypes) { + removeSemType = SemTypes.union(removeSemType, removeType.semType()); + } - if (!matchExists) { - remainingValueSpace.add(valueExpr); + List newValueSpace = new ArrayList<>(); + for (SemNamedType semNamedType : originalType.valueSpace) { + if (!SemTypes.isSubtype(semTypeCtx, semNamedType.semType(), removeSemType)) { + newValueSpace.add(semNamedType); } } - if (remainingValueSpace.isEmpty()) { + if (newValueSpace.isEmpty()) { return symTable.semanticError; } @@ -5864,9 +3182,21 @@ private BType getRemainingType(BFiniteType originalType, List removeTypes originalType.tsymbol.pkgID, null, originalType.tsymbol.owner, originalType.tsymbol.pos, VIRTUAL); - BFiniteType intersectingFiniteType = new BFiniteType(finiteTypeSymbol, remainingValueSpace); - finiteTypeSymbol.type = intersectingFiniteType; - return intersectingFiniteType; + BFiniteType ft = new BFiniteType(finiteTypeSymbol, newValueSpace.toArray(SemNamedType[]::new)); + finiteTypeSymbol.type = ft; + return ft; + } + + public SemType getNilLiftType(SemType t) { + return Core.diff(t, PredefinedType.NIL); + } + + public SemType getErrorLiftType(SemType t) { + return Core.diff(t, PredefinedType.ERROR); + } + + public SemType getNilAndErrorLiftType(SemType t) { + return Core.diff(t, Core.union(PredefinedType.NIL, PredefinedType.ERROR)); } public BType getSafeType(BType bType, boolean liftNil, boolean liftError) { @@ -5876,16 +3206,16 @@ public BType getSafeType(BType bType, boolean liftNil, boolean liftError) { if (liftNil) { switch (type.tag) { case TypeTags.JSON: - return new BJSONType((BJSONType) type, false); + return BJSONType.newNilLiftedBJSONType((BJSONType) type); case TypeTags.ANY: - return new BAnyType(type.tag, type.tsymbol, false); + return BAnyType.newNilLiftedBAnyType(); case TypeTags.ANYDATA: - return new BAnydataType((BAnydataType) type, false); + return BAnydataType.newNilLiftedBAnydataType((BAnydataType) type); case TypeTags.READONLY: if (liftError) { return symTable.anyAndReadonly; } - return new BReadonlyType(type.tag, type.tsymbol, false); + return BReadonlyType.newNilLiftedBReadonlyType(); } } @@ -5895,7 +3225,7 @@ public BType getSafeType(BType bType, boolean liftNil, boolean liftError) { BUnionType unionType = (BUnionType) type; LinkedHashSet memTypes = new LinkedHashSet<>(unionType.getMemberTypes()); - BUnionType errorLiftedType = BUnionType.create(null, memTypes); + BUnionType errorLiftedType = BUnionType.create(typeEnv(), null, memTypes); if (liftNil) { errorLiftedType.remove(symTable.nilType); @@ -5909,7 +3239,7 @@ public BType getSafeType(BType bType, boolean liftNil, boolean liftError) { } } memTypes = bTypes; - errorLiftedType = BUnionType.create(null, memTypes); + errorLiftedType = BUnionType.create(typeEnv(), null, memTypes); } if (errorLiftedType.getMemberTypes().size() == 1) { @@ -5980,8 +3310,7 @@ public boolean isAllowedConstantType(BType type) { yield true; } case TypeTags.FINITE -> { - BLangExpression finiteValue = ((BFiniteType) type).getValueSpace().toArray(new BLangExpression[0])[0]; - yield isAllowedConstantType(finiteValue.getBType()); + yield isAllowedConstantType(SemTypeHelper.broadTypes(type.semType(), symTable).iterator().next()); } default -> false; }; @@ -6037,61 +3366,8 @@ public boolean isSubTypeOfErrorOrNilContainingNil(BUnionType type) { return false; } - for (BType memType : type.getMemberTypes()) { - BType referredMemType = getImpliedType(memType); - if (referredMemType.tag != TypeTags.NIL && referredMemType.tag != TypeTags.ERROR) { - return false; - } - } - return true; - } - - /** - * Type vector of size two, to hold the source and the target types. - * - * @since 0.982.0 - */ - private static class TypePair { - BType sourceType; - BType targetType; - - public TypePair(BType sourceType, BType targetType) { - this.sourceType = sourceType; - this.targetType = targetType; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof TypePair other)) { - return false; - } - - return this.sourceType.equals(other.sourceType) && this.targetType.equals(other.targetType); - } - - @Override - public int hashCode() { - return Objects.hash(sourceType, targetType); - } - } - - /** - * A functional interface for parameterizing the type of type checking that needs to be done on the source and - * target types. - * - * @since 0.995.0 - */ - private interface TypeEqualityPredicate { - boolean test(BType source, BType target, Set unresolvedTypes); - } - - /** - * A functional interface to validate numeric, string or xml type existence. - * - * @since 2201.1.0 - */ - private interface TypeExistenceValidationFunction { - boolean validate(BType type); + BasicTypeBitSet nilOrError = (BasicTypeBitSet) Core.union(PredefinedType.NIL, PredefinedType.ERROR); + return SemTypeHelper.isSubtypeSimpleNotNever(type, nilOrError); } public boolean hasFillerValue(BType type) { @@ -6115,7 +3391,7 @@ public boolean hasFillerValue(BType type) { case TypeTags.ARRAY: return checkFillerValue((BArrayType) type); case TypeTags.FINITE: - return checkFillerValue((BFiniteType) type); + return hasFiller(type.semType()); case TypeTags.UNION: return checkFillerValue((BUnionType) type); case TypeTags.OBJECT: @@ -6156,45 +3432,42 @@ private boolean checkFillerValue(BObjectType type) { } /** - * This will handle two types. Singleton : As singleton can have one value that value should it self be a valid fill - * value Union : 1. if nil is a member it is the fill values 2. else all the values should belong to same type and - * the default value for that type should be a member of the union precondition : value space should have at least - * one element + * Checks whether a SemType has a filler value. + *

+ * Note: this is similar to computeFiller() in nBallerina + *

+ * 1. if type contains nil, nil is the filler value.
+ * 2. if all values belong to a single basic type B, and the filler value for B also included in the values.
+ * 3. if type is a singleton, it is the filler value. + *

* - * @param type BFiniteType union or finite - * @return boolean whether type has a valid filler value or not + * @param t SemType to be checked + * @return whether there is a filler value */ - private boolean checkFillerValue(BFiniteType type) { - if (type.isNullable()) { - return true; - } - if (type.getValueSpace().size() == 1) { // For singleton types, that value is the implicit initial value + private boolean hasFiller(SemType t) { + if (Core.containsNil(t)) { return true; } - Iterator iterator = type.getValueSpace().iterator(); - BLangExpression firstElement = iterator.next(); - boolean defaultFillValuePresent = isImplicitDefaultValue(firstElement); - - while (iterator.hasNext()) { - BLangExpression value = iterator.next(); - if (!isSameBasicType(value.getBType(), firstElement.getBType())) { - return false; - } - if (!defaultFillValuePresent && isImplicitDefaultValue(value)) { - defaultFillValuePresent = true; - } - } - return defaultFillValuePresent; + return hasImplicitDefaultValue(t) || Core.singleShape(t).isPresent(); } - private boolean hasImplicitDefaultValue(Set valueSpace) { - for (BLangExpression expression : valueSpace) { - if (isImplicitDefaultValue(expression)) { - return true; - } + private boolean hasImplicitDefaultValue(SemType t) { + BasicTypeBitSet bitSet = Core.widenToBasicTypes(t); + Object value = null; + if (bitSet.equals(PredefinedType.BOOLEAN)) { + value = false; + } else if (bitSet.equals(PredefinedType.INT)) { + value = (long) 0; + } else if (bitSet.equals(PredefinedType.DECIMAL)) { + value = BigDecimal.valueOf(0); + } else if (bitSet.equals(PredefinedType.FLOAT)) { + value = (double) 0; + } else if (bitSet.equals(PredefinedType.STRING)) { + value = ""; } - return false; + + return value != null && (t instanceof BasicTypeBitSet || Core.containsConst(t, value)); } private boolean checkFillerValue(BUnionType type) { @@ -6211,9 +3484,9 @@ private boolean checkFillerValue(BUnionType type) { for (BType member : getAllTypes(type, true)) { if (member.tag == TypeTags.FINITE) { - Set uniqueValues = getValueTypes(((BFiniteType) member).getValueSpace()); - memberTypes.addAll(uniqueValues); - if (!hasFillerValue && hasImplicitDefaultValue(((BFiniteType) member).getValueSpace())) { + Set broadTypes = SemTypeHelper.broadTypes((BFiniteType) member, symTable); + memberTypes.addAll(broadTypes); + if (!hasFillerValue && hasImplicitDefaultValue(member.semType())) { hasFillerValue = true; } } else { @@ -6256,32 +3529,6 @@ private boolean isIntegerSubTypeTag(int typeTag) { return TypeTags.isIntegerTypeTag(typeTag) || typeTag == TypeTags.BYTE; } - private Set getValueTypes(Set valueSpace) { - Set uniqueType = new HashSet<>(); - for (BLangExpression expression : valueSpace) { - uniqueType.add(expression.getBType()); - } - return uniqueType; - } - - private boolean isImplicitDefaultValue(BLangExpression expression) { - if ((expression.getKind() == NodeKind.LITERAL) || (expression.getKind() == NodeKind.NUMERIC_LITERAL)) { - BLangLiteral literalExpression = (BLangLiteral) expression; - BType literalExprType = literalExpression.getBType(); - Object value = literalExpression.getValue(); - return switch (literalExprType.getKind()) { - case INT, BYTE -> value.equals(0L); - case STRING -> value == null || value.equals(""); - case DECIMAL -> value.equals(String.valueOf(0)) || value.equals(0L); - case FLOAT -> value.equals(String.valueOf(0.0)); - case BOOLEAN -> value.equals(Boolean.FALSE); - case NIL -> true; - default -> false; - }; - } - return false; - } - private boolean checkFillerValue(BRecordType type) { for (BField field : type.fields.values()) { if (Symbols.isFlagOn(field.symbol.flags, Flags.OPTIONAL)) { @@ -6295,138 +3542,146 @@ private boolean checkFillerValue(BRecordType type) { } private boolean checkFillerValue(BArrayType type) { - if (type.size == -1) { + if (type.getSize() == -1) { return true; } return hasFillerValue(type.eType); } /** - * Get result type of the query output. + * Check whether a type is an ordered type. * - * @param type type of query expression. - * @return result type. + * @param type type to be checked + * @return boolean whether the type is an ordered type or not */ - public BType resolveExprType(BType type) { - switch (type.tag) { - case TypeTags.STREAM: - return ((BStreamType) type).constraint; - case TypeTags.TABLE: - return ((BTableType) type).constraint; - case TypeTags.ARRAY: - return ((BArrayType) type).eType; - case TypeTags.UNION: - List exprTypes = new ArrayList<>(((BUnionType) type).getMemberTypes()); - for (BType returnType : exprTypes) { - switch (returnType.tag) { - case TypeTags.STREAM: - return ((BStreamType) returnType).constraint; - case TypeTags.TABLE: - return ((BTableType) returnType).constraint; - case TypeTags.ARRAY: - return ((BArrayType) returnType).eType; - case TypeTags.STRING: - case TypeTags.XML: - return returnType; - } + public boolean isOrderedType(BType type) { + return isOrderedType(type.semType()); + } + + /** + * Checks whether a SemType is an ordered type. + *
+ *

+ * A type is an ordered type if all values belong to one of (), int?, boolean?, decimal?, float?, string? types. + * Additionally, + *

    + *
  • [T...] is ordered, if T is ordered;
  • + *
  • [] is ordered;
  • + *
  • [T, rest] is ordered if T is ordered and [rest] is ordered.
  • + *
+ * + * @param t SemType to be checked + * @return boolean + */ + public boolean isOrderedType(SemType t) { + assert !Core.isNever(t); + SemType tButNil = Core.diff(t, PredefinedType.NIL); + BasicTypeBitSet basicTypeBitSet = Core.widenToBasicTypes(tButNil); + if (SemTypes.isSubtypeSimple(basicTypeBitSet, PredefinedType.SIMPLE_OR_STRING)) { + int bitCount = SemTypeHelper.bitCount(basicTypeBitSet.bitset); + return bitCount <= 1; + } + + if (SemTypes.isSubtypeSimple(tButNil, PredefinedType.LIST)) { + ListMemberTypes lmTypes = Core.listAllMemberTypesInner(typeCtx(), t); + for (SemType lmType : lmTypes.semTypes()) { + if (!isOrderedType(lmType)) { + return false; } - default: - return type; + } + return true; } + + return false; + } + + boolean comparable(BType t1, BType t2) { + return comparable(t1.semType(), t2.semType()); } /** - * Check whether a type is an ordered type. + * Checks whether a SemType pair is comparable. + *
+ *

+ * Note: this is similar to comparable() in nBallerina. However, nBallerina API does not have + * "There must be an ordered type to which the static type of both operands belong" part from spec, implemented + *

* - * @param type type. - * @param hasCycle whether there is a cycle. - * @return boolean whether the type is an ordered type or not. + * @param t1 first semType + * @param t2 second semType + * @return boolean */ - public boolean isOrderedType(BType type, boolean hasCycle) { - type = getImpliedType(type); - switch (type.tag) { - case TypeTags.UNION: - BUnionType unionType = (BUnionType) type; - if (hasCycle) { - return true; - } - if (unionType.isCyclic) { - hasCycle = true; - } - Set memberTypes = unionType.getMemberTypes(); - boolean allMembersOrdered = false; - BType firstTypeInUnion = getTypeWithEffectiveIntersectionTypes(getImpliedType( - memberTypes.stream().filter(m -> !isNil(m)).findFirst().orElse(memberTypes.iterator().next()))); - if (isNil(firstTypeInUnion)) { - // Union contains only the nil type. - return true; - } - boolean isFirstTypeInUnionFinite = firstTypeInUnion.tag == TypeTags.FINITE; - for (BType memType : memberTypes) { - memType = getImpliedType(memType); - if (isFirstTypeInUnionFinite && memType.tag == TypeTags.FINITE && !isNil(memType)) { - Set valSpace = ((BFiniteType) firstTypeInUnion).getValueSpace(); - BType baseExprType = valSpace.iterator().next().getBType(); - if (!checkValueSpaceHasSameType((BFiniteType) memType, baseExprType)) { - return false; - } - } else if (memType.tag == TypeTags.UNION || memType.tag == TypeTags.ARRAY || - memType.tag == TypeTags.TUPLE) { - if (isSameOrderedType(memType, firstTypeInUnion)) { - allMembersOrdered = true; - continue; - } - return false; - } else if (memType.tag != firstTypeInUnion.tag && !isNil(memType) && - !isIntOrStringType(memType.tag, firstTypeInUnion.tag)) { - return false; - } - allMembersOrdered = isOrderedType(memType, hasCycle); - if (!allMembersOrdered) { - break; - } - } - return allMembersOrdered; - case TypeTags.ARRAY: - BType elementType = ((BArrayType) type).eType; - return isOrderedType(elementType, hasCycle); - case TypeTags.TUPLE: - List tupleMemberTypes = ((BTupleType) type).getTupleTypes(); - for (BType memType : tupleMemberTypes) { - if (!isOrderedType(memType, hasCycle)) { - return false; - } - } - BType restType = ((BTupleType) type).restType; - return restType == null || isOrderedType(restType, hasCycle); - case TypeTags.FINITE: - boolean isValueSpaceOrdered = false; - Set valSpace = ((BFiniteType) type).getValueSpace(); - BType baseExprType = valSpace.iterator().next().getBType(); - for (BLangExpression expr : valSpace) { - if (!checkValueSpaceHasSameType((BFiniteType) type, baseExprType)) { - return false; - } - isValueSpaceOrdered = isOrderedType(expr.getBType(), hasCycle); - if (!isValueSpaceOrdered) { - break; - } - } - return isValueSpaceOrdered; - default: - return isSimpleBasicType(type.tag); + boolean comparable(SemType t1, SemType t2) { + assert !Core.isNever(t1) && !Core.isNever(t2); + if (PredefinedType.NIL.equals(t1)) { + return isOrderedType(t2); } + + if (PredefinedType.NIL.equals(t2)) { + return isOrderedType(t1); + } + + SemType tButNil = Core.diff(Core.union(t1, t2), PredefinedType.NIL); + BasicTypeBitSet basicTypeBitSet = Core.widenToBasicTypes(tButNil); + if (SemTypes.isSubtypeSimple(basicTypeBitSet, PredefinedType.SIMPLE_OR_STRING)) { + int bitCount = SemTypeHelper.bitCount(basicTypeBitSet.bitset); + return bitCount <= 1; + } + if (SemTypes.isSubtypeSimple(tButNil, PredefinedType.LIST)) { + return comparableNillableList(typeCtx(), t1, t2); + } + return false; } - private boolean isIntOrStringType(int firstTypeTag, int secondTypeTag) { - return ((TypeTags.isIntegerTypeTag(firstTypeTag) || firstTypeTag == TypeTags.BYTE) && - (TypeTags.isIntegerTypeTag(secondTypeTag) || secondTypeTag == TypeTags.BYTE)) || - ((TypeTags.isStringTypeTag(firstTypeTag)) && (TypeTags.isStringTypeTag(secondTypeTag))); + private boolean comparableNillableList(Context cx, SemType t1, SemType t2) { + SemTypePair semPair = SemTypePair.from(t1, t2); + Boolean b = cx.comparableMemo.get(semPair); + if (b != null) { + return b; + } + + ListMemberTypes lmTypes1 = Core.listAllMemberTypesInner(cx, t1); + ListMemberTypes lmTypes2 = Core.listAllMemberTypesInner(cx, t2); + CombinedRange[] combinedRanges = combineRanges( + lmTypes1.ranges().toArray(Range[]::new), + lmTypes2.ranges().toArray(Range[]::new) + ); + SemType accum = PredefinedType.NIL; + for (CombinedRange combinedRange : combinedRanges) { + Long i1 = combinedRange.i1(); + Long i2 = combinedRange.i2(); + if (i1 == null) { + SemType lmType = lmTypes2.semTypes().get(Math.toIntExact(i2)); + if (!comparable(accum, lmType)) { + return false; + } + accum = Core.union(accum, lmType); + continue; + } + + if (i2 == null) { + SemType lmType = lmTypes1.semTypes().get(Math.toIntExact(i1)); + if (!comparable(accum, lmType)) { + return false; + } + accum = Core.union(accum, lmType); + continue; + } + + + if (!comparable(lmTypes1.semTypes().get(Math.toIntExact(i1)), + lmTypes2.semTypes().get(Math.toIntExact(i2)))) { + cx.comparableMemo.put(semPair, false); + return false; + } + } + cx.comparableMemo.put(semPair, true); + return true; } public boolean isSubTypeOfSimpleBasicTypeOrString(BType bType) { return isAssignable(getImpliedType(bType), - BUnionType.create(null, symTable.nilType, symTable.booleanType, symTable.intType, + BUnionType.create(typeEnv(), null, symTable.nilType, symTable.booleanType, symTable.intType, symTable.floatType, symTable.decimalType, symTable.stringType)); } @@ -6455,69 +3710,25 @@ public BType findCompatibleType(BType type) { yield findCompatibleType(memberTypes.iterator().next()); } default -> { - Set valueSpace = ((BFiniteType) type).getValueSpace(); - yield findCompatibleType(valueSpace.iterator().next().getBType()); + Set broadTypes = SemTypeHelper.broadTypes(type.semType(), symTable); + assert broadTypes.size() == 1; // all values should belong to a single basic type + yield broadTypes.iterator().next(); } }; } public boolean isNonNilSimpleBasicTypeOrString(BType bType) { - BType type = getImpliedType(bType); - if (type.tag == TypeTags.UNION) { - Set memberTypes = ((BUnionType) type).getMemberTypes(); - for (BType member : memberTypes) { - BType memType = getImpliedType(member); - if (memType.tag == TypeTags.FINITE || memType.tag == TypeTags.UNION) { - isNonNilSimpleBasicTypeOrString(memType); - continue; - } - if (memType.tag == TypeTags.NIL || !isSimpleBasicType(memType.tag)) { - return false; - } - } - return true; - } else if (type.tag == TypeTags.FINITE) { - for (BLangExpression expression: ((BFiniteType) type).getValueSpace()) { - BType exprType = getImpliedType(expression.getBType()); - if (exprType.tag == TypeTags.NIL || !isSimpleBasicType(exprType.tag)) { - return false; - } - } - return true; - } - return type.tag != TypeTags.NIL && isSimpleBasicType(type.tag); + return SemTypeHelper.isSubtypeSimpleNotNever(bType, + (BasicTypeBitSet) Core.diff(PredefinedType.SIMPLE_OR_STRING, PredefinedType.NIL)); } public boolean isSubTypeOfReadOnlyOrIsolatedObjectUnion(BType bType) { - BType type = getImpliedType(bType); - if (isInherentlyImmutableType(type) || Symbols.isFlagOn(type.flags, Flags.READONLY)) { - return true; - } - - int tag = type.tag; - - if (tag == TypeTags.OBJECT) { - return isIsolated(type); - } - - if (tag != TypeTags.UNION) { - return false; - } - - for (BType memberType : ((BUnionType) type).getMemberTypes()) { - if (!isSubTypeOfReadOnlyOrIsolatedObjectUnion(memberType)) { - return false; - } - } - return true; - } - - private boolean isIsolated(BType type) { - return Symbols.isFlagOn(type.flags, Flags.ISOLATED); + return SemTypes.isSubtype(semTypeCtx, bType.semType(), + SemTypes.union(PredefinedType.VAL_READONLY, createIsolatedObject(semTypeCtx))); } private boolean isImmutable(BType type) { - return Symbols.isFlagOn(type.flags, Flags.READONLY); + return Symbols.isFlagOn(type.getFlags(), Flags.READONLY); } BType getTypeWithoutNil(BType type) { @@ -6542,11 +3753,19 @@ BType getTypeWithoutNil(BType type) { return nonNilTypes.get(0); } - return BUnionType.create(null, new LinkedHashSet<>(nonNilTypes)); + return BUnionType.create(typeEnv(), null, new LinkedHashSet<>(nonNilTypes)); } public boolean isFixedLengthTuple(BTupleType bTupleType) { - return bTupleType.restType == null || isNeverTypeOrStructureTypeWithARequiredNeverMember(bTupleType.restType); + return isFixedLengthList(bTupleType); + } + + public boolean isFixedLengthList(BType type) { + // Using int:MIN_VALUE to project the rest type. + // This checks the type of effectively infinite list member, which should be the rest type. + SemType rest = Core.listMemberTypeInnerVal(semTypeCtx, type.semType(), + IntSubtype.intConst(Long.MAX_VALUE)); + return Core.isNever(rest); } public boolean isNeverTypeOrStructureTypeWithARequiredNeverMember(BType type) { @@ -6611,25 +3830,18 @@ boolean isNeverTypeOrStructureTypeWithARequiredNeverMember(BType type, Set memberTypes = ((BUnionType) type).getMemberTypes(); - return memberTypes.stream().allMatch(this::isNeverType); - } - return false; + return Core.isNever(type.semType()); } boolean isSingletonType(BType bType) { BType type = getImpliedType(bType); - return type.tag == TypeTags.FINITE && ((BFiniteType) type).getValueSpace().size() == 1; + return type.tag == TypeTags.FINITE && Core.singleShape(type.semType()).isPresent(); } boolean isSameSingletonType(BFiniteType type1, BFiniteType type2) { - BLangLiteral expr1 = (BLangLiteral) type1.getValueSpace().iterator().next(); - BLangLiteral expr2 = (BLangLiteral) type2.getValueSpace().iterator().next(); - return expr1.value.equals(expr2.value); + SemType t1 = type1.semType(); + SemType t2 = type2.semType(); + return SemTypes.isSameType(semTypeCtx, t1, t2); } public static void addImmutableType(SymbolTable symTable, PackageID packageId, @@ -6672,11 +3884,19 @@ public static Optional getImmutableType(SymbolTable symTable, return Optional.empty(); } + public static Name getImmutableTypeName(String origName) { + if (origName.isEmpty()) { + return Names.EMPTY; + } + + return Names.fromString("(".concat(origName).concat(AND_READONLY_SUFFIX).concat(")")); + } + public static String getPackageIdString(PackageID packageID) { return packageID.isTestPkg ? packageID.toString() + "_testable" : packageID.toString(); } - private static class ListenerValidationModel { + private class ListenerValidationModel { private final Types types; private final SymbolTable symtable; private final BType serviceNameType; @@ -6690,7 +3910,8 @@ public ListenerValidationModel(Types types, SymbolTable symTable) { this.types = types; this.symtable = symTable; this.serviceNameType = - BUnionType.create(null, symtable.stringType, symtable.arrayStringType, symtable.nilType); + BUnionType.create(symTable.typeEnv(), null, symtable.stringType, symtable.arrayStringType, + symtable.nilType); } boolean isValidListener() { @@ -6795,21 +4016,7 @@ private boolean checkAttachMethod(BAttachedFunction func) { } private boolean isServiceObject(BType bType) { - BType type = getImpliedType(bType); - if (type.tag == TypeTags.UNION) { - for (BType memberType : ((BUnionType) type).getMemberTypes()) { - if (!isServiceObject(memberType)) { - return false; - } - } - return true; - } - - if (type.tag != TypeTags.OBJECT) { - return false; - } - - return Symbols.isService(type.tsymbol); + return types.isSubtype(bType, createServiceObject(semTypeCtx)); } } @@ -7068,9 +4275,8 @@ private void populateBasicTypes(BType type, Set basicTypes) { basicTypes.add(BasicTypes.OBJECT); return; case TypeTags.FINITE: - for (BLangExpression expression : ((BFiniteType) type).getValueSpace()) { - populateBasicTypes(expression.getBType(), basicTypes); - } + SemType semType = type.semType(); + populateBasicTypes(semType, basicTypes); return; case TypeTags.HANDLE: basicTypes.add(BasicTypes.HANDLE); @@ -7083,6 +4289,35 @@ private void populateBasicTypes(BType type, Set basicTypes) { } } + private void populateBasicTypes(SemType t, Set basicTypes) { + int bitset; + if (t instanceof BasicTypeBitSet b) { + bitset = b.bitset; + } else { + ComplexSemType cst = (ComplexSemType) t; + bitset = cst.all() | cst.some(); + } + + if ((bitset & PredefinedType.NIL.bitset) != 0) { + basicTypes.add(BasicTypes.NIL); + } + if ((bitset & PredefinedType.BOOLEAN.bitset) != 0) { + basicTypes.add(BasicTypes.BOOLEAN); + } + if ((bitset & PredefinedType.INT.bitset) != 0) { + basicTypes.add(BasicTypes.INT); + } + if ((bitset & PredefinedType.FLOAT.bitset) != 0) { + basicTypes.add(BasicTypes.FLOAT); + } + if ((bitset & PredefinedType.DECIMAL.bitset) != 0) { + basicTypes.add(BasicTypes.DECIMAL); + } + if ((bitset & PredefinedType.STRING.bitset) != 0) { + basicTypes.add(BasicTypes.STRING); + } + } + private enum BasicTypes { NIL, BOOLEAN, @@ -7214,4 +4449,14 @@ public boolean isMappingConstructorCompatibleType(BType type) { int tag = getImpliedType(type).tag; return tag == TypeTags.RECORD || tag == TypeTags.MAP; } + + // Maybe it is a better idea to directly make Env accessible via the CompilerContext but that means SemType module + // will have a dependency on compiler + public Env typeEnv() { + return semTypeCtx.env; + } + + public Context typeCtx() { + return semTypeCtx; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java index 5d3bfba21165..a1109b10a97c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/SymbolTable.java @@ -18,6 +18,9 @@ package org.wso2.ballerinalang.compiler.semantics.model; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.PackageID; import org.ballerinalang.model.symbols.SymbolOrigin; @@ -26,7 +29,6 @@ import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.diagnostic.BLangDiagnosticLocation; import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BConstructorSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BErrorTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BOperatorSymbol; @@ -48,8 +50,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BReadonlyType; @@ -76,7 +76,6 @@ import org.wso2.ballerinalang.util.Lists; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Optional; @@ -114,49 +113,45 @@ public class SymbolTable { public final Scope rootScope; public final BType noType = new BNoType(TypeTags.NONE); - public final BType nilType = new BNilType(); - public final BType neverType = new BNeverType(); - public final BType intType = new BType(TypeTags.INT, null, Flags.READONLY); - public final BType byteType = new BType(TypeTags.BYTE, null, Flags.READONLY); - public final BType floatType = new BType(TypeTags.FLOAT, null, Flags.READONLY); - public final BType decimalType = new BType(TypeTags.DECIMAL, null, Flags.READONLY); - public final BType stringType = new BType(TypeTags.STRING, null, Flags.READONLY); - public final BType booleanType = new BType(TypeTags.BOOLEAN, null, Flags.READONLY); - - public final BType anyType = new BAnyType(TypeTags.ANY, null); - public final BMapType mapType = new BMapType(TypeTags.MAP, anyType, null); - public final BMapType mapStringType = new BMapType(TypeTags.MAP, stringType, null); - - public final BFutureType futureType = new BFutureType(TypeTags.FUTURE, nilType, null); - public final BArrayType arrayType = new BArrayType(anyType); - public final BArrayType byteArrayType = new BArrayType(byteType); - public final BArrayType arrayStringType = new BArrayType(stringType); + public final BType nilType = BType.createNilType(); + public final BType neverType = BType.createNeverType(); + public final BType intType = new BType(TypeTags.INT, null, Flags.READONLY, PredefinedType.INT); + public final BType byteType = new BType(TypeTags.BYTE, null, Flags.READONLY, PredefinedType.BYTE); + public final BType floatType = new BType(TypeTags.FLOAT, null, Flags.READONLY, PredefinedType.FLOAT); + public final BType decimalType = new BType(TypeTags.DECIMAL, null, Flags.READONLY, PredefinedType.DECIMAL); + public final BType stringType = new BType(TypeTags.STRING, null, Flags.READONLY, PredefinedType.STRING); + public final BType booleanType = new BType(TypeTags.BOOLEAN, null, Flags.READONLY, PredefinedType.BOOLEAN); + + public final BType anyType = new BAnyType(); + public final BMapType mapType; + public final BMapType mapStringType; + + public final BFutureType futureType; + public final BArrayType arrayType; + public final BArrayType byteArrayType; + public final BArrayType arrayStringType; BVarSymbol varSymbol = new BVarSymbol(0, null, null, noType, null, null, SymbolOrigin.VIRTUAL); - public final BType tupleType = new BTupleType(Lists.of(new BTupleMember(noType, varSymbol))); - public final BType recordType = new BRecordType(null); - public final BType stringArrayType = new BArrayType(stringType); - public final BType handleType = new BHandleType(TypeTags.HANDLE, null); - public final BTypedescType typeDesc = new BTypedescType(this.anyType, null); - public final BType readonlyType = new BReadonlyType(TypeTags.READONLY, null); - public final BType pathParamAllowedType = BUnionType.create(null, - intType, stringType, floatType, booleanType, decimalType); + public final BType tupleType; + public final BType recordType; + public final BType stringArrayType; + public final BType handleType = new BHandleType(); + public final BTypedescType typeDesc; + public final BType readonlyType = new BReadonlyType(); + public final BType pathParamAllowedType; public final BIntersectionType anyAndReadonly; public BUnionType anyAndReadonlyOrError; - public final BType errorIntersectionType = new BErrorType(null, null); - - public final BType semanticError = new BType(TypeTags.SEMANTIC_ERROR, null); - public final BType nullSet = new BType(TypeTags.NULL_SET, null); - public final BType invokableType = new BInvokableType(null, null, null, null); + public final BType semanticError = new BType(TypeTags.SEMANTIC_ERROR, null, PredefinedType.NEVER); + public final BType nullSet = new BType(TypeTags.NULL_SET, null, PredefinedType.NEVER); + public final BType invokableType; public final BType empty = new BType(TypeTags.EMPTY, null); - public BConstructorSymbol errorConstructor; public BUnionType anyOrErrorType; public BUnionType pureType; public BUnionType errorOrNilType; - public BFiniteType trueType; - public BFiniteType falseType; + public BType trueType; + public BType falseType; public BObjectType intRangeType; public BMapType mapAllType; public BArrayType arrayAllType; @@ -164,23 +159,25 @@ public class SymbolTable { public BObjectType iterableType; // builtin subtypes - public final BIntSubType signed32IntType = new BIntSubType(TypeTags.SIGNED32_INT, Names.SIGNED32); - public final BIntSubType signed16IntType = new BIntSubType(TypeTags.SIGNED16_INT, Names.SIGNED16); - public final BIntSubType signed8IntType = new BIntSubType(TypeTags.SIGNED8_INT, Names.SIGNED8); - public final BIntSubType unsigned32IntType = new BIntSubType(TypeTags.UNSIGNED32_INT, Names.UNSIGNED32); - public final BIntSubType unsigned16IntType = new BIntSubType(TypeTags.UNSIGNED16_INT, Names.UNSIGNED16); - public final BIntSubType unsigned8IntType = new BIntSubType(TypeTags.UNSIGNED8_INT, Names.UNSIGNED8); - public final BStringSubType charStringType = new BStringSubType(TypeTags.CHAR_STRING, Names.CHAR); - public final BXMLSubType xmlElementType = new BXMLSubType(TypeTags.XML_ELEMENT, Names.XML_ELEMENT); - public final BXMLSubType xmlPIType = new BXMLSubType(TypeTags.XML_PI, Names.XML_PI); - public final BXMLSubType xmlCommentType = new BXMLSubType(TypeTags.XML_COMMENT, Names.XML_COMMENT); - public final BXMLSubType xmlTextType = new BXMLSubType(TypeTags.XML_TEXT, Names.XML_TEXT, Flags.READONLY); - public final BRegexpType regExpType = new BRegexpType(TypeTags.REGEXP, Names.REGEXP_TYPE); + public final BIntSubType signed32IntType = BIntSubType.SIGNED32; + public final BIntSubType signed16IntType = BIntSubType.SIGNED16; + public final BIntSubType signed8IntType = BIntSubType.SIGNED8; + public final BIntSubType unsigned32IntType = BIntSubType.UNSIGNED32; + public final BIntSubType unsigned16IntType = BIntSubType.UNSIGNED16; + public final BIntSubType unsigned8IntType = BIntSubType.UNSIGNED8; + + public final BStringSubType charStringType = BStringSubType.CHAR; + + public final BXMLSubType xmlElementType = BXMLSubType.XML_ELEMENT; + public final BXMLSubType xmlPIType = BXMLSubType.XML_PI; + public final BXMLSubType xmlCommentType = BXMLSubType.XML_COMMENT; + public final BXMLSubType xmlTextType = BXMLSubType.XML_TEXT; + + public final BRegexpType regExpType = new BRegexpType(); public final BType xmlNeverType = new BXMLType(neverType, null); - public final BType xmlElementSeqType = new BXMLType(xmlElementType, null); - public final BType xmlType = new BXMLType(BUnionType.create(null, xmlElementType, xmlCommentType, - xmlPIType, xmlTextType), null); + public final BType xmlType; + public final BType xmlElementSeqType = new BXMLType(xmlElementType, null); public BAnydataType anydataType; public BArrayType arrayAnydataType; @@ -225,6 +222,7 @@ public class SymbolTable { private final Names names; private final Types types; + public Map pkgEnvMap = new HashMap<>(); public Map predeclaredModules = new HashMap<>(); public Map> immutableTypeMaps = new HashMap<>(); @@ -244,7 +242,7 @@ private SymbolTable(CompilerContext context) { this.names = Names.getInstance(context); this.types = Types.getInstance(context); - this.rootPkgNode = (BLangPackage) TreeBuilder.createPackageNode(); + this.rootPkgNode = (BLangPackage) TreeBuilder.createPackageNode(types.typeEnv()); this.rootPkgSymbol = new BPackageSymbol(PackageID.ANNOTATIONS, null, null, BUILTIN); this.builtinPos = new BLangDiagnosticLocation(Names.EMPTY.value, -1, -1, -1, -1); @@ -263,15 +261,10 @@ private SymbolTable(CompilerContext context) { initializeType(decimalType, TypeKind.DECIMAL.typeName(), BUILTIN); initializeType(stringType, TypeKind.STRING.typeName(), BUILTIN); initializeType(booleanType, TypeKind.BOOLEAN.typeName(), BUILTIN); - initializeType(xmlType, TypeKind.XML.typeName(), BUILTIN); - initializeType(mapType, TypeKind.MAP.typeName(), VIRTUAL); - initializeType(mapStringType, TypeKind.MAP.typeName(), VIRTUAL); - initializeType(futureType, TypeKind.FUTURE.typeName(), BUILTIN); initializeType(anyType, TypeKind.ANY.typeName(), BUILTIN); initializeType(nilType, TypeKind.NIL.typeName(), BUILTIN); initializeType(neverType, TypeKind.NEVER.typeName(), BUILTIN); initializeType(handleType, TypeKind.HANDLE.typeName(), BUILTIN); - initializeType(typeDesc, TypeKind.TYPEDESC.typeName(), BUILTIN); initializeType(readonlyType, TypeKind.READONLY.typeName(), BUILTIN); // Define subtypes @@ -296,22 +289,41 @@ private SymbolTable(CompilerContext context) { falseLiteral.setBType(this.booleanType); falseLiteral.value = Boolean.FALSE; + arrayType = new BArrayType(types.typeEnv(), anyType); + byteArrayType = new BArrayType(types.typeEnv(), byteType); + arrayStringType = new BArrayType(types.typeEnv(), stringType); + stringArrayType = new BArrayType(types.typeEnv(), stringType); + + mapType = new BMapType(typeEnv(), TypeTags.MAP, anyType, null); + mapStringType = new BMapType(typeEnv(), TypeTags.MAP, stringType, null); + initializeType(mapType, TypeKind.MAP.typeName(), VIRTUAL); + initializeType(mapStringType, TypeKind.MAP.typeName(), VIRTUAL); + + pathParamAllowedType = BUnionType.create(types.typeEnv(), null, + intType, stringType, floatType, booleanType, decimalType); + tupleType = new BTupleType(types.typeEnv(), Lists.of(new BTupleMember(noType, varSymbol))); + recordType = new BRecordType(typeEnv(), null); + invokableType = new BInvokableType(types.typeEnv(), List.of(), null, null, null); + + xmlType = new BXMLType(BUnionType.create(types.typeEnv(), null, xmlElementType, xmlCommentType, + xmlPIType, xmlTextType), null); + futureType = new BFutureType(types.typeEnv(), nilType, null, PredefinedType.FUTURE); + typeDesc = new BTypedescType(types.typeEnv(), this.anyType, null, PredefinedType.TYPEDESC); + initializeType(xmlType, TypeKind.XML.typeName(), BUILTIN); + initializeType(futureType, TypeKind.FUTURE.typeName(), BUILTIN); + initializeType(typeDesc, TypeKind.TYPEDESC.typeName(), BUILTIN); + defineCyclicUnionBasedInternalTypes(); - BTypeSymbol finiteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, Flags.PUBLIC, - Names.fromString("$anonType$TRUE"), - rootPkgNode.packageID, null, rootPkgNode.symbol.owner, - this.builtinPos, VIRTUAL); - this.trueType = new BFiniteType(finiteTypeSymbol, new HashSet<>() {{ - add(trueLiteral); - }}); + BTypeSymbol trueFiniteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, Flags.PUBLIC, + Names.fromString("$anonType$TRUE"), rootPkgNode.packageID, null, rootPkgNode.symbol.owner, + this.builtinPos, VIRTUAL); + this.trueType = BFiniteType.newSingletonBFiniteType(trueFiniteTypeSymbol, SemTypes.booleanConst(true)); BTypeSymbol falseFiniteTypeSymbol = Symbols.createTypeSymbol(SymTag.FINITE_TYPE, Flags.PUBLIC, Names.fromString("$anonType$FALSE"), rootPkgNode.packageID, null, rootPkgNode.symbol.owner, this.builtinPos, VIRTUAL); - this.falseType = new BFiniteType(falseFiniteTypeSymbol, new HashSet<>() {{ - add(falseLiteral); - }}); + this.falseType = BFiniteType.newSingletonBFiniteType(falseFiniteTypeSymbol, SemTypes.booleanConst(false)); this.anyAndReadonly = ImmutableTypeCloner.getImmutableIntersectionType(this.anyType, this, names, this.types, @@ -319,7 +331,7 @@ private SymbolTable(CompilerContext context) { initializeType(this.anyAndReadonly, this.anyAndReadonly.effectiveType.name.getValue(), BUILTIN); // Initialize the invokable type - this.invokableType.flags = Flags.ANY_FUNCTION; + this.invokableType.setFlags(Flags.ANY_FUNCTION); BInvokableTypeSymbol tSymbol = Symbols.createInvokableTypeSymbol(SymTag.FUNCTION_TYPE, Flags.ANY_FUNCTION, rootPkgSymbol.pkgID, this.invokableType, rootPkgNode.symbol.scope.owner, builtinPos, BUILTIN); tSymbol.params = null; @@ -331,7 +343,7 @@ private SymbolTable(CompilerContext context) { } private void defineReadonlyCompoundType() { - anyAndReadonlyOrError = BUnionType.create(null, anyAndReadonly, errorType); + anyAndReadonlyOrError = BUnionType.create(typeEnv(), null, anyAndReadonly, errorType); } public BType getTypeFromTag(int tag) { @@ -639,9 +651,7 @@ private void defineIntegerUnaryOperations() { } private BUnionType getNilableBType(BType type) { - BUnionType nilableType = BUnionType.create(null, type, nilType); - nilableType.setNullable(true); - return nilableType; + return BUnionType.create(typeEnv(), null, type, nilType); } private void defineNilableIntegerUnaryOperations() { @@ -1103,7 +1113,7 @@ private void defineUnaryOperator(OperatorKind kind, private void defineOperator(Name name, List paramTypes, BType retType) { - BInvokableType opType = new BInvokableType(paramTypes, retType, null); + BInvokableType opType = new BInvokableType(typeEnv(), paramTypes, retType, null); BOperatorSymbol symbol = new BOperatorSymbol(name, rootPkgSymbol.pkgID, opType, rootPkgSymbol, this.builtinPos, BUILTIN); @@ -1123,7 +1133,7 @@ private void defineCyclicUnionBasedInternalTypes() { } private void defineCloneableCyclicTypeAndDependentTypes() { - cloneableType = BUnionType.create(null, readonlyType, xmlType); + cloneableType = BUnionType.create(typeEnv(), null, readonlyType, xmlType); addCyclicArrayMapTableOfMapMembers(cloneableType); // `cloneableType` and its symbol gets replaced by `Cloneable` type defined in lang value module. To prevent @@ -1132,31 +1142,31 @@ private void defineCloneableCyclicTypeAndDependentTypes() { cloneableType.tsymbol = new BTypeSymbol(SymTag.TYPE, Flags.PRIVATE, Names.CLONEABLE, rootPkgSymbol.pkgID, cloneableType, rootPkgSymbol, builtinPos, BUILTIN); - detailType = new BMapType(TypeTags.MAP, cloneableType, null); - errorType = new BErrorType(null, detailType); + detailType = new BMapType(typeEnv(), TypeTags.MAP, cloneableType, null); + errorType = new BErrorType(typeEnv(), null, detailType); errorType.tsymbol = new BErrorTypeSymbol(SymTag.ERROR, Flags.PUBLIC, Names.ERROR, rootPkgSymbol.pkgID, errorType, rootPkgSymbol, builtinPos, BUILTIN); - errorOrNilType = BUnionType.create(null, errorType, nilType); - anyOrErrorType = BUnionType.create(null, anyType, errorType); + errorOrNilType = BUnionType.create(typeEnv(), null, errorType, nilType); + anyOrErrorType = BUnionType.create(typeEnv(), null, anyType, errorType); - mapAllType = new BMapType(TypeTags.MAP, anyOrErrorType, null); - arrayAllType = new BArrayType(anyOrErrorType); + mapAllType = new BMapType(typeEnv(), TypeTags.MAP, anyOrErrorType, null); + arrayAllType = new BArrayType(typeEnv(), anyOrErrorType); typeDesc.constraint = anyOrErrorType; futureType.constraint = anyOrErrorType; - pureType = BUnionType.create(null, anydataType, errorType); - streamType = new BStreamType(TypeTags.STREAM, pureType, nilType, null); - tableType = new BTableType(TypeTags.TABLE, pureType, null); + pureType = BUnionType.create(typeEnv(), null, anydataType, errorType); + streamType = new BStreamType(typeEnv(), TypeTags.STREAM, pureType, nilType, null); + tableType = new BTableType(typeEnv(), pureType, null); initializeType(streamType, TypeKind.STREAM.typeName(), BUILTIN); initializeType(tableType, TypeKind.TABLE.typeName(), BUILTIN); } private void addCyclicArrayMapTableOfMapMembers(BUnionType unionType) { - BArrayType arrayCloneableType = new BArrayType(unionType); - BMapType mapCloneableType = new BMapType(TypeTags.MAP, unionType, null); - BType tableMapCloneableType = new BTableType(TypeTags.TABLE, mapCloneableType, null); + BArrayType arrayCloneableType = new BArrayType(typeEnv(), unionType); + BMapType mapCloneableType = new BMapType(typeEnv(), TypeTags.MAP, unionType, null); + BType tableMapCloneableType = new BTableType(typeEnv(), mapCloneableType, null); unionType.add(arrayCloneableType); unionType.add(mapCloneableType); unionType.add(tableMapCloneableType); @@ -1164,15 +1174,16 @@ private void addCyclicArrayMapTableOfMapMembers(BUnionType unionType) { } private void defineJsonCyclicTypeAndDependentTypes() { - BUnionType jsonInternal = BUnionType.create(null, nilType, booleanType, intType, floatType, decimalType, + BUnionType jsonInternal = + BUnionType.create(typeEnv(), null, nilType, booleanType, intType, floatType, decimalType, stringType); - BArrayType arrayJsonTypeInternal = new BArrayType(jsonInternal); - BMapType mapJsonTypeInternal = new BMapType(TypeTags.MAP, jsonInternal, null); + BArrayType arrayJsonTypeInternal = new BArrayType(typeEnv(), jsonInternal); + BMapType mapJsonTypeInternal = new BMapType(typeEnv(), TypeTags.MAP, jsonInternal, null); jsonInternal.add(arrayJsonTypeInternal); jsonInternal.add(mapJsonTypeInternal); jsonInternal.isCyclic = true; - jsonType = new BJSONType(jsonInternal); + jsonType = new BJSONType(types.typeCtx(), jsonInternal); PackageID pkgID = rootPkgSymbol.pkgID; Optional immutableType = Types.getImmutableType(this, pkgID, jsonInternal); if (immutableType.isPresent()) { @@ -1181,16 +1192,17 @@ private void defineJsonCyclicTypeAndDependentTypes() { jsonType.tsymbol = new BTypeSymbol(SymTag.TYPE, Flags.PUBLIC, Names.JSON, pkgID, jsonType, rootPkgSymbol, builtinPos, BUILTIN); - arrayJsonType = new BArrayType(jsonType); - mapJsonType = new BMapType(TypeTags.MAP, jsonType, null); + arrayJsonType = new BArrayType(typeEnv(), jsonType); + mapJsonType = new BMapType(typeEnv(), TypeTags.MAP, jsonType, null); } private void defineAnydataCyclicTypeAndDependentTypes() { - BUnionType anyDataInternal = BUnionType.create(null, nilType, booleanType, intType, floatType, decimalType, + BUnionType anyDataInternal = + BUnionType.create(typeEnv(), null, nilType, booleanType, intType, floatType, decimalType, stringType, xmlType); addCyclicArrayMapTableOfMapMembers(anyDataInternal); - anydataType = new BAnydataType(anyDataInternal); + anydataType = new BAnydataType(types.typeCtx(), anyDataInternal); PackageID pkgID = rootPkgSymbol.pkgID; Optional immutableType = Types.getImmutableType(this, pkgID, anyDataInternal); if (immutableType.isPresent()) { @@ -1199,10 +1211,15 @@ private void defineAnydataCyclicTypeAndDependentTypes() { anydataType.tsymbol = new BTypeSymbol(SymTag.TYPE, Flags.PUBLIC, Names.ANYDATA, pkgID, anydataType, rootPkgSymbol, builtinPos, BUILTIN); - arrayAnydataType = new BArrayType(anydataType); - mapAnydataType = new BMapType(TypeTags.MAP, anydataType, null); - anydataOrReadonly = BUnionType.create(null, anydataType, readonlyType); + arrayAnydataType = new BArrayType(typeEnv(), anydataType); + mapAnydataType = new BMapType(typeEnv(), TypeTags.MAP, anydataType, null); + anydataOrReadonly = BUnionType.create(typeEnv(), null, anydataType, readonlyType); initializeType(mapAnydataType, TypeKind.MAP.typeName(), VIRTUAL); } + + public Env typeEnv() { + assert types.typeEnv() != null; + return types.typeEnv(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/TypeVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/TypeVisitor.java index 86e84538855b..9cf5163e54c2 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/TypeVisitor.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/TypeVisitor.java @@ -22,7 +22,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; @@ -32,7 +31,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; @@ -47,70 +45,77 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BTypedescType; import org.wso2.ballerinalang.compiler.semantics.model.types.BUnionType; import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; +import org.wso2.ballerinalang.compiler.util.TypeTags; /** * Visit ballerina types and maps them to instances T. * * @since 0.995.0 */ -public interface TypeVisitor { +public abstract class TypeVisitor { - void visit(BAnnotationType bAnnotationType); + public abstract void visit(BAnnotationType bAnnotationType); - void visit(BArrayType bArrayType); + public abstract void visit(BArrayType bArrayType); - void visit(BBuiltInRefType bBuiltInRefType); + public abstract void visit(BAnyType bAnyType); - void visit(BAnyType bAnyType); + public abstract void visit(BAnydataType bAnydataType); - void visit(BAnydataType bAnydataType); + public abstract void visit(BErrorType bErrorType); - void visit(BErrorType bErrorType); + public abstract void visit(BFiniteType bFiniteType); - void visit(BFiniteType bFiniteType); + public abstract void visit(BInvokableType bInvokableType); - void visit(BInvokableType bInvokableType); + public abstract void visit(BJSONType bjsonType); - void visit(BJSONType bjsonType); + public abstract void visit(BMapType bMapType); - void visit(BMapType bMapType); + public abstract void visit(BStreamType bStreamType); - void visit(BStreamType bStreamType); + public abstract void visit(BTypedescType bTypedescType); - void visit(BTypedescType bTypedescType); + public abstract void visit(BTypeReferenceType bTypeReferenceType); - void visit(BTypeReferenceType bTypeReferenceType); + public abstract void visit(BParameterizedType bTypedescType); - void visit(BParameterizedType bTypedescType); + public abstract void visit(BNeverType bNeverType); - void visit(BNeverType bNeverType); + public abstract void visitNilType(BType bType); - void visit(BNilType bNilType); + public abstract void visit(BNoType bNoType); - void visit(BNoType bNoType); + public abstract void visit(BPackageType bPackageType); - void visit(BPackageType bPackageType); + public abstract void visit(BStructureType bStructureType); - void visit(BStructureType bStructureType); + public abstract void visit(BTupleType bTupleType); - void visit(BTupleType bTupleType); + public abstract void visit(BUnionType bUnionType); - void visit(BUnionType bUnionType); + public abstract void visit(BIntersectionType bIntersectionType); - void visit(BIntersectionType bIntersectionType); + public abstract void visit(BXMLType bxmlType); - void visit(BXMLType bxmlType); + public abstract void visit(BTableType bTableType); - void visit(BTableType bTableType); + public abstract void visit(BRecordType bRecordType); - void visit(BRecordType bRecordType); + public abstract void visit(BObjectType bObjectType); - void visit(BObjectType bObjectType); + public void visit(BType type) { + if (type == null) { // TODO: see if we can remove + return; + } - void visit(BType bType); + switch (type.tag) { + case TypeTags.NIL: + visitNilType(type); + } + } - void visit(BFutureType bFutureType); - - void visit(BHandleType bHandleType); + public abstract void visit(BFutureType bFutureType); + public abstract void visit(BHandleType bHandleType); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/UniqueTypeVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/UniqueTypeVisitor.java index 3f574d7c81f6..273ca80f2dc6 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/UniqueTypeVisitor.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/UniqueTypeVisitor.java @@ -22,7 +22,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; @@ -33,11 +32,11 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BJSONType; import org.wso2.ballerinalang.compiler.semantics.model.types.BMapType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNeverType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BNilType; import org.wso2.ballerinalang.compiler.semantics.model.types.BNoType; import org.wso2.ballerinalang.compiler.semantics.model.types.BObjectType; import org.wso2.ballerinalang.compiler.semantics.model.types.BPackageType; import org.wso2.ballerinalang.compiler.semantics.model.types.BParameterizedType; +import org.wso2.ballerinalang.compiler.semantics.model.types.BReadonlyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BRecordType; import org.wso2.ballerinalang.compiler.semantics.model.types.BStreamType; import org.wso2.ballerinalang.compiler.semantics.model.types.BStructureType; @@ -56,75 +55,75 @@ * @param return type of visit methods * @since Swan Lake */ -public interface UniqueTypeVisitor { +public abstract class UniqueTypeVisitor { - default R visit(UniqueTypeVisitor visitor) { + R visit(UniqueTypeVisitor visitor) { return visitor.visit(this); } - boolean isVisited(BType type); + public abstract boolean isVisited(BType type); - void reset(); + public abstract void reset(); - R visit(BAnnotationType bAnnotationType); + public abstract R visit(BAnnotationType bAnnotationType); - R visit(BArrayType bArrayType); + public abstract R visit(BArrayType bArrayType); - R visit(BBuiltInRefType bBuiltInRefType); + public abstract R visit(BReadonlyType bReadonlyType); - R visit(BAnyType bAnyType); + public abstract R visit(BAnyType bAnyType); - R visit(BAnydataType bAnydataType); + public abstract R visit(BAnydataType bAnydataType); - R visit(BErrorType bErrorType); + public abstract R visit(BErrorType bErrorType); - R visit(BFiniteType bFiniteType); + public abstract R visit(BFiniteType bFiniteType); - R visit(BInvokableType bInvokableType); + public abstract R visit(BInvokableType bInvokableType); - R visit(BJSONType bjsonType); + public abstract R visit(BJSONType bjsonType); - R visit(BMapType bMapType); + public abstract R visit(BMapType bMapType); - R visit(BStreamType bStreamType); + public abstract R visit(BStreamType bStreamType); - R visit(BTypedescType bTypedescType); + public abstract R visit(BTypedescType bTypedescType); - R visit(BParameterizedType bTypedescType); + public abstract R visit(BParameterizedType bTypedescType); - R visit(BNeverType bNeverType); + public abstract R visit(BNeverType bNeverType); - R visit(BNilType bNilType); + public abstract R visitNilType(BType bType); - R visit(BNoType bNoType); + public abstract R visit(BNoType bNoType); - R visit(BPackageType bPackageType); + public abstract R visit(BPackageType bPackageType); - R visit(BStructureType bStructureType); + public abstract R visit(BStructureType bStructureType); - R visit(BTupleType bTupleType); + public abstract R visit(BTupleType bTupleType); - R visit(BUnionType bUnionType); + public abstract R visit(BUnionType bUnionType); - R visit(BIntersectionType bIntersectionType); + public abstract R visit(BIntersectionType bIntersectionType); - R visit(BTypeReferenceType bTypeReferenceType); + public abstract R visit(BTypeReferenceType bTypeReferenceType); - R visit(BXMLType bxmlType); + public abstract R visit(BXMLType bxmlType); - R visit(BTableType bTableType); + public abstract R visit(BTableType bTableType); - R visit(BRecordType bRecordType); + public abstract R visit(BRecordType bRecordType); - R visit(BObjectType bObjectType); + public abstract R visit(BObjectType bObjectType); - R visit(BType bType); + public abstract R visit(BType bType); - R visit(BFutureType bFutureType); + public abstract R visit(BFutureType bFutureType); - R visit(BHandleType bHandleType); + public abstract R visit(BHandleType bHandleType); - R visit(BIntSubType intSubType); + public abstract R visit(BIntSubType intSubType); - R visit(BXMLSubType bxmlSubType); + public abstract R visit(BXMLSubType bxmlSubType); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BAttachedFunction.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BAttachedFunction.java index 4bff7d56f36b..d425df9b2822 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BAttachedFunction.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BAttachedFunction.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.semantics.model.symbols; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.SemType; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.util.Name; import org.wso2.ballerinalang.util.Flags; @@ -45,13 +46,17 @@ public BAttachedFunction(Name funcName, BInvokableSymbol symbol, BInvokableType public String toString() { StringBuilder sb = new StringBuilder(); - if (Symbols.isFlagOn(type.flags, Flags.ISOLATED)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.ISOLATED)) { sb.append("isolated "); } - if (Symbols.isFlagOn(type.flags, Flags.TRANSACTIONAL)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.TRANSACTIONAL)) { sb.append("transactional "); } sb.append("function ").append(funcName).append(" ").append(type.getTypeSignature()); return sb.toString(); } + + public SemType semType() { + return type.semType(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BResourceFunction.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BResourceFunction.java index 7cc5fd7eea01..7b552f438272 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BResourceFunction.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/symbols/BResourceFunction.java @@ -18,6 +18,10 @@ package org.wso2.ballerinalang.compiler.semantics.model.symbols; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.definition.FunctionDefinition; import org.ballerinalang.model.symbols.SymbolKind; import org.wso2.ballerinalang.compiler.semantics.model.types.BInvokableType; import org.wso2.ballerinalang.compiler.semantics.model.types.BType; @@ -25,6 +29,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; /** * {@code BResourceFunction} represents a resource function in Ballerina. @@ -77,4 +82,17 @@ public String toString() { type.paramTypes = originalParamTypes; return sb.toString(); } + + @Override + public SemType semType() { + List params = new ArrayList<>(); + params.add(SemTypes.stringConst(accessor.value)); + for (var each : pathSegmentSymbols) { + params.add(Objects.requireNonNullElse(each.type.semType(), PredefinedType.NEVER)); + } + for (var param : this.type.paramTypes) { + params.add(Objects.requireNonNullElse(param.semType(), PredefinedType.NEVER)); + } + return this.type.getSemTypeWithParams(params, new FunctionDefinition()); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnyType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnyType.java index 0991d500ead1..ff9a6f4efe09 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnyType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnyType.java @@ -17,40 +17,54 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; import org.ballerinalang.model.Name; import org.ballerinalang.model.types.SelectivelyImmutableReferenceType; import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import static io.ballerina.types.PredefinedType.ANY; +import static io.ballerina.types.PredefinedType.VAL_READONLY; + /** * @since 0.94 */ -public class BAnyType extends BBuiltInRefType implements SelectivelyImmutableReferenceType { +public class BAnyType extends BType implements SelectivelyImmutableReferenceType { + private boolean nullable = true; - public BAnyType(int tag, BTypeSymbol tsymbol) { - super(tag, tsymbol); + public BAnyType() { + this(ANY); + } + + public BAnyType(Name name, long flag) { + this(name, flag, Symbols.isFlagOn(flag, Flags.READONLY) ? Core.intersect(ANY, VAL_READONLY) : ANY); } - public BAnyType(int tag, BTypeSymbol tsymbol, Name name, long flag) { - super(tag, tsymbol); + private BAnyType(Name name, long flags, SemType semType) { + super(TypeTags.ANY, null, semType); this.name = name; - this.flags = flag; + this.setFlags(flags); } - public BAnyType(int tag, BTypeSymbol tsymbol, boolean nullable) { - super(tag, tsymbol); - this.nullable = nullable; + private BAnyType(SemType semType) { + super(TypeTags.ANY, null, semType); } - public BAnyType(int tag, BTypeSymbol tsymbol, Name name, long flags, boolean nullable) { - super(tag, tsymbol); - this.name = name; - this.flags = flags; - this.nullable = nullable; + public static BAnyType newNilLiftedBAnyType() { + BAnyType result = new BAnyType(Core.diff(ANY, PredefinedType.NIL)); + result.nullable = false; + return result; + } + + public static BAnyType newImmutableBAnyType() { + return new BAnyType(Types.getImmutableTypeName(TypeKind.ANY.typeName()), Flags.READONLY); } @Override @@ -75,7 +89,7 @@ public void accept(TypeVisitor visitor) { @Override public String toString() { - return !Symbols.isFlagOn(flags, Flags.READONLY) ? getKind().typeName() : + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? getKind().typeName() : getKind().typeName().concat(" & readonly"); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnydataType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnydataType.java index cdfe4f488060..4c2c3b9b08ca 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnydataType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BAnydataType.java @@ -17,6 +17,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; import org.ballerinalang.model.Name; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -33,52 +37,54 @@ * @since 0.985.0 */ public class BAnydataType extends BUnionType { - + private boolean nullable; private static final int INITIAL_CAPACITY = 10; + private final Context typeCtx; - public BAnydataType(BTypeSymbol tsymbol, Name name, long flags) { - this(tsymbol, name, flags, true); - } - - public BAnydataType(BTypeSymbol tsymbol, boolean nullable) { - super(tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), nullable, false); + private BAnydataType(Context typeCtx, BTypeSymbol tsymbol, Name name, long flags, boolean nullable) { + super(typeCtx.env, tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), false); this.tag = TypeTags.ANYDATA; - this.isCyclic = true; - } - - public BAnydataType(BTypeSymbol tsymbol, Name name, long flags, boolean nullable) { - super(tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), nullable, false); - this.tag = TypeTags.ANYDATA; - this.flags = flags; + this.setFlags(flags); this.name = name; this.isCyclic = true; + this.nullable = nullable; + this.typeCtx = typeCtx; } - public BAnydataType(BUnionType type) { - super(type.tsymbol, new LinkedHashSet<>(type.memberTypes.size()), type.isNullable(), - Symbols.isFlagOn(type.flags, Flags.READONLY)); + public BAnydataType(Context typeCtx, BUnionType type) { + super(type.env, type.tsymbol, new LinkedHashSet<>(type.memberTypes.size()), + Symbols.isFlagOn(type.getFlags(), Flags.READONLY)); this.tag = TypeTags.ANYDATA; this.isCyclic = true; this.name = type.name; - this.flags = type.flags; + this.setFlags(type.getFlags()); + this.nullable = type.isNullable(); mergeUnionType(type); + this.typeCtx = typeCtx; } - public BAnydataType(BAnydataType type, boolean nullable) { - super(type.tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), nullable, - Symbols.isFlagOn(type.flags, Flags.READONLY)); - this.flags = type.flags; - this.tag = TypeTags.ANYDATA; - this.isCyclic = true; - mergeUnionType(type); + public static BAnydataType newNilLiftedBAnydataType(BAnydataType type) { + BAnydataType result = new BAnydataType(type.typeCtx, type); + result.nullable = false; + return result; + } + + public static BAnydataType newImmutableBAnydataType(BAnydataType type, BTypeSymbol typeSymbol, Name name, + boolean nullable) { + return new BAnydataType(type.typeCtx, typeSymbol, name, type.getFlags() | Flags.READONLY, nullable); } @Override public String toString() { - return !Symbols.isFlagOn(flags, Flags.READONLY) ? getKind().typeName() : + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? getKind().typeName() : getKind().typeName().concat(" & readonly"); } + @Override + public boolean isNullable() { + return nullable; + } + @Override public TypeKind getKind() { return TypeKind.ANYDATA; @@ -94,4 +100,15 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + @Override + public SemType semType() { + SemType anydata = Core.createAnydata(typeCtx); + if (!nullable) { + anydata = Core.diff(anydata, PredefinedType.NIL); + } + if (Symbols.isFlagOn(getFlags(), Flags.READONLY)) { + anydata = Core.intersect(anydata, PredefinedType.VAL_READONLY); + } + return anydata; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BArrayType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BArrayType.java index be1b2986bb68..855dfe0c1919 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BArrayType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BArrayType.java @@ -17,6 +17,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.ListDefinition; import org.ballerinalang.model.types.ArrayType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -26,40 +30,63 @@ import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.VAL; + /** * @since 0.94 */ public class BArrayType extends BType implements ArrayType { + + private static final int NO_FIXED_SIZE = -1; public BType eType; - public int size = -1; + private int size = NO_FIXED_SIZE; public BArrayState state = BArrayState.OPEN; public BArrayType mutableType; + private final Env env; + private ListDefinition ld = null; - public BArrayType(BType elementType) { + public BArrayType(Env env, BType elementType) { super(TypeTags.ARRAY, null); this.eType = elementType; + this.env = env; } - public BArrayType(BType elementType, BTypeSymbol tsymbol) { + public BArrayType(Env env, BType elementType, BTypeSymbol tsymbol) { super(TypeTags.ARRAY, tsymbol); this.eType = elementType; + this.env = env; } - public BArrayType(BType elementType, BTypeSymbol tsymbol, int size, BArrayState state) { + public BArrayType(Env env, BType elementType, BTypeSymbol tsymbol, int size, BArrayState state) { super(TypeTags.ARRAY, tsymbol); this.eType = elementType; this.size = size; this.state = state; + this.env = env; } - public BArrayType(BType elementType, BTypeSymbol tsymbol, int size, BArrayState state, long flags) { + public BArrayType(Env env, BType elementType, BTypeSymbol tsymbol, int size, BArrayState state, long flags) { super(TypeTags.ARRAY, tsymbol, flags); this.eType = elementType; this.size = size; this.state = state; + this.env = env; + } + + /** + * It is required to reset {@link #ld} when the type gets mutated. + * This method is used for that. e.g. When changing Flags.READONLY + */ + protected void restLd() { + ld = null; } @Override @@ -67,6 +94,17 @@ public int getSize() { return size; } + public void setSize(int size) { + if (ld != null) { + // This is dangerous since someone have already captured the SemType may use it in the future. But we have + // cases where we actually do "proper" (i.e not accidental type checks like `isNullable`) type checks and + // then update the size. One option for this may be to poison the semtype, so that using it after this + // point trigger an exception. + ld = null; + } + this.size = size; + } + @Override public BType getElementType() { return eType; @@ -104,6 +142,66 @@ public String toString() { sb.append("[]"); } } - return !Symbols.isFlagOn(flags, Flags.READONLY) ? sb.toString() : sb.append(" & readonly").toString(); + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? sb.toString() : sb.append(" & readonly").toString(); + } + + private boolean hasTypeHoles() { + return eType instanceof BNoType; + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + ld = null; + } + + // If the element type has a semtype component then it will be represented by that component otherwise with never. + // This means we depend on properly partitioning types to semtype components. Also, we need to ensure member types + // are "ready" when we call this + @Override + public SemType semType() { + if (ld != null) { + return ld.getSemType(env); + } + ld = new ListDefinition(); + if (hasTypeHoles()) { + return ld.defineListTypeWrapped(env, VAL); + } + SemType elementTypeSemType = eType.semType(); + if (elementTypeSemType == null) { + elementTypeSemType = NEVER; + } + boolean isReadonly = Symbols.isFlagOn(getFlags(), Flags.READONLY); + CellAtomicType.CellMutability mut = isReadonly ? CELL_MUT_NONE : CELL_MUT_LIMITED; + // Not entirely sure if I understand this correctly, + // if size == -1 it means T[] + // if size < 0 && not -1 it means T[abs(size)] (and size was inferred) + // else it is the fixed size + if (size != NO_FIXED_SIZE) { + return ld.defineListTypeWrapped(env, List.of(elementTypeSemType), Math.abs(size), NEVER, mut); + } else { + return ld.defineListTypeWrapped(env, List.of(), 0, elementTypeSemType, mut); + } + } + + // This is to ensure call to isNullable won't call semType. In case this is a member of a recursive union otherwise + // this will have an invalid list type since parent union type call this while it is filling its members + @Override + public boolean isNullable() { + return false; + } + + @Override + public void setFlags(long flags) { + super.setFlags(flags); + restLd(); + } + + @Override + public void addFlags(long flags) { + super.addFlags(flags); + restLd(); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BBuiltInRefType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BBuiltInRefType.java deleted file mode 100644 index cb8965ec71e1..000000000000 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BBuiltInRefType.java +++ /dev/null @@ -1,67 +0,0 @@ -/* -* Copyright (c) 2017, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. -* -* WSO2 Inc. licenses this file to you under the Apache License, -* Version 2.0 (the "License"); you may not use this file except -* in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, -* software distributed under the License is distributed on an -* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -* KIND, either express or implied. See the License for the -* specific language governing permissions and limitations -* under the License. -*/ -package org.wso2.ballerinalang.compiler.semantics.model.types; - -import org.ballerinalang.model.types.ReferenceType; -import org.ballerinalang.model.types.TypeKind; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; - -import static org.wso2.ballerinalang.compiler.util.TypeTags.ANY; -import static org.wso2.ballerinalang.compiler.util.TypeTags.ANYDATA; -import static org.wso2.ballerinalang.compiler.util.TypeTags.FUTURE; -import static org.wso2.ballerinalang.compiler.util.TypeTags.JSON; -import static org.wso2.ballerinalang.compiler.util.TypeTags.MAP; -import static org.wso2.ballerinalang.compiler.util.TypeTags.STREAM; -import static org.wso2.ballerinalang.compiler.util.TypeTags.TABLE; -import static org.wso2.ballerinalang.compiler.util.TypeTags.TYPEDESC; -import static org.wso2.ballerinalang.compiler.util.TypeTags.XML; - -/** - * @since 0.94 - */ -public class BBuiltInRefType extends BType implements ReferenceType { - - public BBuiltInRefType(int tag, BTypeSymbol tsymbol) { - super(tag, tsymbol); - } - - public BBuiltInRefType(int tag, BTypeSymbol tsymbol, long flags) { - super(tag, tsymbol, flags); - } - - @Override - public R accept(BTypeVisitor visitor, T t) { - return visitor.visit(this, t); - } - - @Override - public TypeKind getKind() { - return switch (tag) { - case JSON -> TypeKind.JSON; - case XML -> TypeKind.XML; - case STREAM -> TypeKind.STREAM; - case TABLE -> TypeKind.TABLE; - case ANY -> TypeKind.ANY; - case ANYDATA -> TypeKind.ANYDATA; - case MAP -> TypeKind.MAP; - case FUTURE -> TypeKind.FUTURE; - case TYPEDESC -> TypeKind.TYPEDESC; - default -> TypeKind.OTHER; - }; - } -} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BErrorType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BErrorType.java index 89e0892f9910..972299acb5db 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BErrorType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BErrorType.java @@ -17,12 +17,25 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.ErrorType; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; +import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + /** * Represents error type in Ballerina. * @@ -36,15 +49,23 @@ public class BErrorType extends BType implements ErrorType { private static final String ERROR = "error<"; private static final String CLOSE_ERROR = ">"; - public BErrorType(BTypeSymbol tSymbol, BType detailType) { + private final Env env; + public int distinctId = -1; + private final DistinctIdSupplier distinctIdSupplier; + + public BErrorType(Env env, BTypeSymbol tSymbol, BType detailType) { super(TypeTags.ERROR, tSymbol, Flags.READONLY); this.detailType = detailType; this.typeIdSet = BTypeIdSet.emptySet(); + this.env = env; + this.distinctIdSupplier = new DistinctIdSupplier(env); } - public BErrorType(BTypeSymbol tSymbol) { + public BErrorType(Env env, BTypeSymbol tSymbol) { super(TypeTags.ERROR, tSymbol, Flags.READONLY); this.typeIdSet = BTypeIdSet.emptySet(); + this.env = env; + this.distinctIdSupplier = new DistinctIdSupplier(env); } @Override @@ -70,4 +91,58 @@ public String toString() { } return ERROR + detailType + CLOSE_ERROR; } + + public void setDistinctId() { + if (Symbols.isFlagOn(this.getFlags(), Flags.DISTINCT)) { + distinctId = env.distinctAtomCountGetAndIncrement(); + } + } + + @Override + public SemType semType() { + return distinctIdWrapper(semTypeInner()); + } + + SemType distinctIdWrapper(SemType semTypeInner) { + return distinctIdSupplier.get().stream().map(SemTypes::errorDistinct).reduce(semTypeInner, Core::intersect); + } + + private SemType semTypeInner() { + if (this.semType != null) { + return this.semType; + } + + if (detailType == null || detailType.semType() == null) { + // semtype will be null for semantic error + this.semType = PredefinedType.ERROR; + } else { + SemType detail = detailType.semType(); + this.semType = SemTypes.errorDetail(detail); + } + return this.semType; + } + + private final class DistinctIdSupplier implements Supplier> { + + private List ids = null; + private static final Map> allocatedIds = + Collections.synchronizedMap(new WeakHashMap<>()); + private final Env env; + + private DistinctIdSupplier(Env env) { + this.env = env; + allocatedIds.putIfAbsent(env, new ConcurrentHashMap<>()); + } + + public synchronized List get() { + if (ids != null) { + return ids; + } + Map envAllocatedIds = allocatedIds.get(env); + ids = typeIdSet.getAll().stream() + .map(each -> envAllocatedIds.computeIfAbsent(each, (key) -> env.distinctAtomCountGetAndIncrement())) + .toList(); + return ids; + } + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFiniteType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFiniteType.java index 45c5512ef81f..100134cd1fec 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFiniteType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFiniteType.java @@ -18,45 +18,61 @@ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.types.FiniteType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; -import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; +import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; +import java.util.Optional; import java.util.StringJoiner; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.Core.singleShape; +import static io.ballerina.types.SemTypes.isSubtypeSimple; +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_STRING; + /** * {@code BFiniteType} represents the finite type in Ballerina. * */ -public class BFiniteType extends BType implements FiniteType { - - private final Set valueSpace; - private boolean nullable = false; - public Boolean isAnyData = null; +public class BFiniteType extends BType { + public SemNamedType[] valueSpace; - public BFiniteType(BTypeSymbol tsymbol) { - super(TypeTags.FINITE, tsymbol); - valueSpace = new LinkedHashSet<>(); - this.flags |= Flags.READONLY; + public BFiniteType(BTypeSymbol tsymbol, SemNamedType[] valueSpace) { + super(TypeTags.FINITE, tsymbol, Flags.READONLY); + this.valueSpace = valueSpace; + assert validValueSpace(valueSpace); } - public BFiniteType(BTypeSymbol tsymbol, Set valueSpace) { - super(TypeTags.FINITE, tsymbol); - this.valueSpace = valueSpace; - this.flags |= Flags.READONLY; + public static BFiniteType newSingletonBFiniteType(BTypeSymbol tsymbol, SemType singletonSemType) { + return new BFiniteType(tsymbol, new SemNamedType[]{ + new SemNamedType(singletonSemType, Optional.empty()) + }); } - @Override - public Set getValueSpace() { - return Collections.unmodifiableSet(valueSpace); + private boolean validValueSpace(SemNamedType[] valueSpace) { + for (SemNamedType semNamedType : valueSpace) { + if (singleShape(semNamedType.semType()).isEmpty()) { + return false; + } + } + return true; } @Override @@ -64,6 +80,22 @@ public TypeKind getKind() { return TypeKind.FINITE; } + @Override + public SemType semType() { + if (this.semType == null) { + this.semType = computeResultantSemType(valueSpace); + } + return this.semType; + } + + private SemType computeResultantSemType(SemNamedType[] valueSpace) { + SemType s = PredefinedType.NEVER; + for (SemNamedType semNamedType : valueSpace) { + s = SemTypes.union(s, semNamedType.semType()); + } + return s; + } + @Override public void accept(TypeVisitor visitor) { visitor.visit(this); @@ -74,37 +106,47 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + @SuppressWarnings("OptionalGetWithoutIsPresent") // xxxSubtypeSingleValue() are guaranteed to have a value @Override public String toString() { StringJoiner joiner = new StringJoiner("|"); - for (BLangExpression value : this.valueSpace) { - switch (value.getBType().tag) { - case TypeTags.FLOAT: - joiner.add(value + "f"); - break; - case TypeTags.DECIMAL: - joiner.add(value + "d"); - break; - case TypeTags.STRING: - case TypeTags.CHAR_STRING: - joiner.add("\"" + value + "\""); - break; - default: - joiner.add(value.toString()); - } - } - return joiner.toString(); - } + for (SemNamedType semNamedType : valueSpace) { + SemType semType = semNamedType.semType(); + Optional name = semNamedType.optName(); - @Override - public boolean isNullable() { - return nullable; - } + if (PredefinedType.NIL.equals(semType)) { + joiner.add(name.orElse(Names.NIL_VALUE.value)); + continue; + } - public void addValue(BLangExpression value) { - this.valueSpace.add(value); - if (!this.nullable && value.getBType() != null && value.getBType().isNullable()) { - this.nullable = true; + ComplexSemType cs = (ComplexSemType) semType; + if (isSubtypeSimple(semType, PredefinedType.BOOLEAN)) { + joiner.add(name.orElse( + BooleanSubtype.booleanSubtypeSingleValue(getComplexSubtypeData(cs, BT_BOOLEAN)).get() ? + Names.TRUE.value : Names.FALSE.value + )); + } else if (isSubtypeSimple(semType, PredefinedType.INT)) { + joiner.add(name.orElse( + Long.toString(IntSubtype.intSubtypeSingleValue(getComplexSubtypeData(cs, BT_INT)).get()) + )); + } else if (isSubtypeSimple(semType, PredefinedType.FLOAT)) { + joiner.add(name.orElse( + Double.toString(FloatSubtype.floatSubtypeSingleValue(getComplexSubtypeData(cs, BT_FLOAT)).get()) + ) + "f"); + } else if (isSubtypeSimple(semType, PredefinedType.DECIMAL)) { + joiner.add(name.orElse( + DecimalSubtype.decimalSubtypeSingleValue(getComplexSubtypeData(cs, BT_DECIMAL)).get() + .toPlainString() + ) + "d"); + } else if (isSubtypeSimple(semType, PredefinedType.STRING)) { + joiner.add("\"" + name.orElse( + StringSubtype.stringSubtypeSingleValue(getComplexSubtypeData(cs, BT_STRING)).get() + ) + "\""); + } else { + throw new IllegalStateException("Unexpected value space type in BFiniteType: " + semType); + } } + + return joiner.toString(); } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFutureType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFutureType.java index 4cb0b0b72cd7..33065fa3c05b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFutureType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BFutureType.java @@ -16,7 +16,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.ConstrainedType; +import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -26,31 +31,48 @@ * * @since 0.965.0 */ -public class BFutureType extends BBuiltInRefType implements ConstrainedType { +public class BFutureType extends BType implements ConstrainedType { public BType constraint; public boolean workerDerivative; + private final Env env; - public BFutureType(int tag, BType constraint, BTypeSymbol tsymbol) { - super(tag, tsymbol); + public BFutureType(Env env, BType constraint, BTypeSymbol tsymbol) { + super(TypeTags.FUTURE, tsymbol); this.constraint = constraint; + this.env = env; } - public BFutureType(int tag, BType constraint, BTypeSymbol tsymbol, boolean workerDerivative) { - this(tag, constraint, tsymbol); + public BFutureType(Env env, BType constraint, BTypeSymbol tsymbol, boolean workerDerivative) { + this(env, constraint, tsymbol); this.workerDerivative = workerDerivative; } + public BFutureType(Env env, BType constraint, BTypeSymbol tsymbol, SemType semType) { + this(env, constraint, tsymbol); + this.semType = semType; + } + @Override public BType getConstraint() { return constraint; } + public void setConstraint(BType constraint) { + this.constraint = constraint; + this.semType = null; + } + @Override public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } - + + @Override + public TypeKind getKind() { + return TypeKind.FUTURE; + } + @Override public String toString() { if (constraint.tag == TypeTags.NONE || constraint.tag == TypeTags.SEMANTIC_ERROR @@ -65,4 +87,18 @@ public String toString() { public void accept(TypeVisitor visitor) { visitor.visit(this); } + + @Override + public SemType semType() { + if (this.semType != null) { + return this.semType; + } + + if (constraint == null || constraint instanceof BNoType) { + this.semType = PredefinedType.FUTURE; + } else { + this.semType = SemTypes.futureContaining(env, constraint.semType()); + } + return this.semType; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BHandleType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BHandleType.java index 8c535433699d..dcc4540cb8e9 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BHandleType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BHandleType.java @@ -17,9 +17,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; /** @@ -29,10 +30,10 @@ * * @since 1.0.0 */ -public class BHandleType extends BBuiltInRefType { +public class BHandleType extends BType { - public BHandleType(int tag, BTypeSymbol tsymbol) { - super(tag, tsymbol, Flags.READONLY); + public BHandleType() { + super(TypeTags.HANDLE, null, Flags.READONLY, PredefinedType.HANDLE); } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntSubType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntSubType.java index 13dd337a6c39..675fa600ccc2 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntSubType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntSubType.java @@ -17,10 +17,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.Name; import org.ballerinalang.model.types.TypeKind; -import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.util.Names; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; /** @@ -30,55 +32,44 @@ */ public class BIntSubType extends BType { - public BIntSubType(int tag, Name name) { + public static final BIntSubType SIGNED32 = new BIntSubType(TypeTags.SIGNED32_INT, Names.SIGNED32, SemTypes.SINT32); + public static final BIntSubType SIGNED16 = new BIntSubType(TypeTags.SIGNED16_INT, Names.SIGNED16, SemTypes.SINT16); + public static final BIntSubType SIGNED8 = new BIntSubType(TypeTags.SIGNED8_INT, Names.SIGNED8, SemTypes.SINT8); - super(tag, null, name, Flags.READONLY); + public static final BIntSubType UNSIGNED32 = new BIntSubType(TypeTags.UNSIGNED32_INT, Names.UNSIGNED32, + SemTypes.UINT32); + public static final BIntSubType UNSIGNED16 = new BIntSubType(TypeTags.UNSIGNED16_INT, Names.UNSIGNED16, + SemTypes.UINT16); + public static final BIntSubType UNSIGNED8 = new BIntSubType(TypeTags.UNSIGNED8_INT, Names.UNSIGNED8, + SemTypes.UINT8); + + private BIntSubType(int tag, Name name, SemType semType) { + super(tag, null, name, Flags.READONLY, semType); } @Override public boolean isNullable() { - return false; } @Override public R accept(BTypeVisitor visitor, T t) { - return visitor.visit(this, t); } @Override public TypeKind getKind() { - return TypeKind.INT; } - @Override - public void accept(TypeVisitor visitor) { - - visitor.visit(this); - } - @Override public String toString() { - return Names.INT.value + Names.ALIAS_SEPARATOR + name; } @Override public String getQualifiedTypeName() { - return Names.BALLERINA_ORG.value + Names.ORG_NAME_SEPARATOR.value + Names.LANG.value + Names.DOT.value + Names.INT.value + Names.ALIAS_SEPARATOR + name; } - - public boolean isAnydata() { - - return true; - } - - public boolean isPureType() { - - return true; - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntersectionType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntersectionType.java index 6c3bba3fcfe5..bd80e473972f 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntersectionType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BIntersectionType.java @@ -17,8 +17,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.IntersectionType; import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; @@ -45,14 +49,14 @@ public BIntersectionType(BTypeSymbol tsymbol, LinkedHashSet types, BType effectiveType) { super(TypeTags.INTERSECTION, tsymbol); this.constituentTypes = toFlatTypeSet(types); - this.effectiveType = effectiveType; for (BType constituentType : this.constituentTypes) { if (constituentType.tag == TypeTags.READONLY) { - this.flags |= Flags.READONLY; + this.addFlags(Flags.READONLY); break; } } + this.effectiveType = effectiveType; } public BIntersectionType(BTypeSymbol tsymbol) { @@ -81,18 +85,13 @@ public void accept(TypeVisitor visitor) { visitor.visit(this); } - @Override - public boolean isNullable() { - return this.effectiveType.isNullable(); - } - @Override public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } public void setConstituentTypes(LinkedHashSet constituentTypes) { - this.constituentTypes = toFlatTypeSet(constituentTypes); + this.constituentTypes = toFlatTypeSet(constituentTypes); } @Override @@ -134,4 +133,34 @@ private static LinkedHashSet toFlatTypeSet(LinkedHashSet types) { public BType getEffectiveType() { return this.effectiveType; } + + /** + * When the type is mutated we need to reset resolved semType. + */ + public void resetSemType() { + this.semType = null; + } + + @Override + public SemType semType() { + // We have to recalculate this everytime since the actual BTypes inside constituent types do mutate and we + // can't detect those mutations. + return computeResultantIntersection(); + } + + private SemType computeResultantIntersection() { + SemType t = PredefinedType.VAL; + for (BType constituentType : this.getConstituentTypes()) { + t = SemTypes.intersect(t, constituentType.semType()); + } + + // TODO: this is a temporary workaround to propagate effective typeIds + BType referredType = Types.getReferredType(this.effectiveType); + if (referredType instanceof BErrorType effErr) { + t = effErr.distinctIdWrapper(t); + } else if (referredType instanceof BObjectType effObj) { + t = effObj.distinctIdWrapper(t); + } + return t; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BInvokableType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BInvokableType.java index 3882ba40fde0..86d23257ed77 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BInvokableType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BInvokableType.java @@ -17,8 +17,17 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.definition.FunctionDefinition; +import io.ballerina.types.definition.FunctionQualifiers; +import io.ballerina.types.definition.ListDefinition; import org.ballerinalang.model.types.InvokableType; import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; @@ -26,7 +35,10 @@ import org.wso2.ballerinalang.util.Flags; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; import java.util.List; +import java.util.Set; /** * @since 0.94 @@ -34,23 +46,39 @@ public class BInvokableType extends BType implements InvokableType { public List paramTypes; + // TODO: make these final public BType restType; public BType retType; + private final Env env; + private FunctionDefinition defn; - public BInvokableType(List paramTypes, BType restType, BType retType, BTypeSymbol tsymbol) { + public BInvokableType(Env env, List paramTypes, BType restType, BType retType, BTypeSymbol tsymbol) { super(TypeTags.INVOKABLE, tsymbol, Flags.READONLY); - this.paramTypes = paramTypes; + this.paramTypes = Collections.unmodifiableList(paramTypes); + assert restType == null || restType instanceof BArrayType || restType.tag == TypeTags.SEMANTIC_ERROR; this.restType = restType; this.retType = retType; + this.env = env; } - public BInvokableType(List paramTypes, BType retType, BTypeSymbol tsymbol) { - this(paramTypes, null, retType, tsymbol); + public BInvokableType(Env env, List paramTypes, BType retType, BTypeSymbol tsymbol) { + this(env, paramTypes, null, retType, tsymbol); } - public BInvokableType(BTypeSymbol tSymbol) { + public BInvokableType(Env env, BTypeSymbol tSymbol) { super(TypeTags.INVOKABLE, tSymbol, Flags.READONLY); - this.paramTypes = new ArrayList<>(); + this.paramTypes = List.of(); + this.env = env; + } + + public void addParamType(BType type) { + List newParams = new ArrayList<>(paramTypes); + newParams.add(type); + paramTypes = Collections.unmodifiableList(newParams); + } + + public void setParamTypes(List paramTypes) { + this.paramTypes = Collections.unmodifiableList(paramTypes); } @Override @@ -76,13 +104,13 @@ public R accept(BTypeVisitor visitor, T t) { @Override public String toString() { String flagStr = ""; - if (Symbols.isFlagOn(flags, Flags.ISOLATED)) { + if (Symbols.isFlagOn(getFlags(), Flags.ISOLATED)) { flagStr = "isolated "; } - if (Symbols.isFlagOn(flags, Flags.TRANSACTIONAL)) { + if (Symbols.isFlagOn(getFlags(), Flags.TRANSACTIONAL)) { flagStr += "transactional "; } - if (Symbols.isFlagOn(flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(getFlags(), Flags.ANY_FUNCTION)) { return flagStr + "function"; } return flagStr + "function " + getTypeSignature(); @@ -97,7 +125,7 @@ public boolean equals(Object o) { return false; } - if (this.flags != that.flags) { + if (this.getFlags() != that.getFlags()) { return false; } @@ -155,4 +183,129 @@ private static String getBTypeListAsString(List typeNames) { public void accept(TypeVisitor visitor) { visitor.visit(this); } + + @Override + public boolean isNullable() { + return false; + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + defn = null; + } + + @Override + public SemType semType() { + if (isFunctionTop()) { + if (Symbols.isFlagOn(getFlags(), Flags.ISOLATED) || Symbols.isFlagOn(getFlags(), Flags.TRANSACTIONAL)) { + FunctionQualifiers qualifiers = + FunctionQualifiers.from(env, Symbols.isFlagOn(getFlags(), Flags.ISOLATED), + Symbols.isFlagOn(getFlags(), Flags.TRANSACTIONAL)); + FunctionDefinition definition = new FunctionDefinition(); + return definition.define(env, PredefinedType.NEVER, PredefinedType.VAL, qualifiers); + } + return PredefinedType.FUNCTION; + } + if (defn != null) { + return defn.getSemType(env); + } + FunctionDefinition fd = new FunctionDefinition(); + this.defn = fd; + List params = this.paramTypes.stream().map(BInvokableType::from).toList(); + return getSemTypeWithParams(params, fd); + } + + public SemType getSemTypeWithParams(List params, FunctionDefinition fd) { + SemType rest; + if (restType instanceof BArrayType arrayType) { + rest = from(arrayType.eType); + } else { + // Is this correct even when type is semantic error? + rest = PredefinedType.NEVER; + } + SemType returnType = resolveReturnType(); + ListDefinition paramListDefinition = new ListDefinition(); + SemType paramTypes = paramListDefinition.defineListTypeWrapped(env, params, params.size(), rest, + CellAtomicType.CellMutability.CELL_MUT_NONE); + // TODO: probably we need to move this method from Types. + boolean isGeneric = Types.containsTypeParams(this); + FunctionQualifiers qualifiers = FunctionQualifiers.from(env, Symbols.isFlagOn(getFlags(), Flags.ISOLATED), + Symbols.isFlagOn(getFlags(), Flags.TRANSACTIONAL)); + if (isGeneric) { + return fd.defineGeneric(env, paramTypes, returnType, qualifiers); + } + return fd.define(env, paramTypes, returnType, qualifiers); + } + + private SemType resolveReturnType() { + if (retType == null) { + return PredefinedType.NIL; + } + SemType innerType = from(retType); + ListDefinition ld = new ListDefinition(); + return ld.tupleTypeWrapped(env, isDependentlyTyped(retType, + new HashSet<>()) ? SemTypes.booleanConst(true) : PredefinedType.BOOLEAN, innerType); + } + + private static boolean isDependentlyTyped(BType returnType, Set visited) { + if (visited.contains(returnType)) { + return false; + } + + visited.add(returnType); + + // it doesn't seem we actually have a flag to check this, may be the correct way to do this is to have a + // method in BType for this, but given this is a temporary thing, this should be enough. + if (returnType instanceof BParameterizedType) { + return true; + } + if (returnType instanceof BUnionType unionType) { + return unionType.getMemberTypes().stream().anyMatch(returnType1 -> + isDependentlyTyped(returnType1, visited)); + } + if (returnType instanceof BMapType mapType) { + return isDependentlyTyped(mapType.constraint, visited); + } + if (returnType instanceof BRecordType recordType) { + return recordType.fields.values().stream().anyMatch(field -> isDependentlyTyped(field.type, visited)) || + isDependentlyTyped(recordType.restFieldType, visited); + } + if (returnType instanceof BArrayType arrayType) { + return isDependentlyTyped(arrayType.eType, visited); + } + if (returnType instanceof BTupleType tupleType) { + return tupleType.getTupleTypes().stream().anyMatch(returnType1 -> isDependentlyTyped(returnType1, visited)); + } + if (returnType instanceof BInvokableType invokableType) { + return invokableType.paramTypes.stream().anyMatch(returnType1 -> + isDependentlyTyped(returnType1, visited)) || + isDependentlyTyped(invokableType.retType, visited) || + isDependentlyTyped(invokableType.restType, visited); + } + if (returnType instanceof BFutureType futureType) { + return isDependentlyTyped(futureType.constraint, visited); + } + if (returnType instanceof BTableType tableType) { + return isDependentlyTyped(tableType.constraint, visited); + } + if (returnType instanceof BStreamType streamType) { + return isDependentlyTyped(streamType.constraint, visited); + } + return false; + } + + private static SemType from(BType type) { + SemType semType = type.semType(); + if (semType == null) { + semType = PredefinedType.NEVER; + } + return semType; + } + + private boolean isFunctionTop() { + return paramTypes.isEmpty() && restType == null && retType == null; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BJSONType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BJSONType.java index 46b3c81d955b..78f81599f2e3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BJSONType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BJSONType.java @@ -17,6 +17,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; @@ -30,37 +34,49 @@ * @since 0.94 */ public class BJSONType extends BUnionType { - + private boolean nullable = true; private static final int INITIAL_CAPACITY = 8; + private final Context typeCtx; - public BJSONType(BJSONType type, boolean nullable) { - super(type.tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), nullable, - Symbols.isFlagOn(type.flags, Flags.READONLY)); + public BJSONType(Context typeCtx, BUnionType type) { + super(type.env, type.tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), Symbols.isFlagOn(type.getFlags(), + Flags.READONLY)); mergeUnionType(type); this.tag = TypeTags.JSON; this.isCyclic = true; + this.typeCtx = typeCtx; } - public BJSONType(BUnionType type) { - super(type.tsymbol, new LinkedHashSet<>(INITIAL_CAPACITY), type.isNullable(), Symbols.isFlagOn(type.flags, - Flags.READONLY)); - mergeUnionType(type); + private BJSONType(Context typeCtx, BTypeSymbol typeSymbol, boolean nullable, long flags) { + super(typeCtx.env, typeSymbol, new LinkedHashSet<>(INITIAL_CAPACITY), Symbols.isFlagOn(flags, Flags.READONLY)); + this.setFlags(flags); this.tag = TypeTags.JSON; + this.isCyclic = true; + this.nullable = nullable; + this.typeCtx = typeCtx; } - public BJSONType(BTypeSymbol typeSymbol, boolean nullable, long flags) { - super(typeSymbol, new LinkedHashSet<>(INITIAL_CAPACITY), nullable, Symbols.isFlagOn(flags, Flags.READONLY)); - this.flags = flags; - this.tag = TypeTags.JSON; - this.isCyclic = true; + public static BJSONType newNilLiftedBJSONType(BJSONType type) { + BJSONType result = new BJSONType(type.typeCtx, type); + result.nullable = false; + return result; + } + + public static BJSONType newImmutableBJSONType(BJSONType type, BTypeSymbol typeSymbol, boolean nullable) { + return new BJSONType(type.typeCtx, typeSymbol, nullable, type.getFlags() | Flags.READONLY); } @Override public String toString() { - return !Symbols.isFlagOn(flags, Flags.READONLY) ? getKind().typeName() : + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? getKind().typeName() : getKind().typeName().concat(" & readonly"); } + @Override + public boolean isNullable() { + return nullable; + } + @Override public TypeKind getKind() { return TypeKind.JSON; @@ -75,4 +91,17 @@ public void accept(TypeVisitor visitor) { public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + + @Override + public SemType semType() { + SemType json = Core.createJson(typeCtx); + // TODO: refer to https://github.com/ballerina-platform/ballerina-lang/issues/43343#issuecomment-2485247172 +// if (!nullable) { +// json = Core.diff(json, PredefinedType.NIL); +// } + if (Symbols.isFlagOn(getFlags(), Flags.READONLY)) { + json = Core.intersect(json, PredefinedType.VAL_READONLY); + } + return json; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BMapType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BMapType.java index 3112abb1d6c8..6efc4a020d33 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BMapType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BMapType.java @@ -17,29 +17,54 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.MappingDefinition; import org.ballerinalang.model.types.ConstrainedType; import org.ballerinalang.model.types.SelectivelyImmutableReferenceType; +import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.VAL; + /** * @since 0.94 */ -public class BMapType extends BBuiltInRefType implements ConstrainedType, SelectivelyImmutableReferenceType { +public class BMapType extends BType implements ConstrainedType, SelectivelyImmutableReferenceType { public BType constraint; public BMapType mutableType; - public BMapType(int tag, BType constraint, BTypeSymbol tsymbol) { + private final Env env; + private MappingDefinition md = null; + + public BMapType(Env env, int tag, BType constraint, BTypeSymbol tsymbol) { super(tag, tsymbol); this.constraint = constraint; + this.env = env; } - public BMapType(int tag, BType constraint, BTypeSymbol tsymbol, long flags) { + public BMapType(Env env, int tag, BType constraint, BTypeSymbol tsymbol, long flags) { super(tag, tsymbol, flags); this.constraint = constraint; + this.env = env; + } + + /** + * It is required to reset {@link #md} when the type gets mutated. + * This method is used for that. e.g. When changing Flags.READONLY + */ + protected void restMd() { + md = null; } @Override @@ -52,6 +77,11 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + @Override + public TypeKind getKind() { + return TypeKind.MAP; + } + @Override public String toString() { String stringRep; @@ -62,11 +92,63 @@ public String toString() { stringRep = super.toString() + "<" + constraint + ">"; } - return !Symbols.isFlagOn(flags, Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); } @Override public void accept(TypeVisitor visitor) { visitor.visit(this); } + + private boolean hasTypeHoles() { + return constraint instanceof BNoType; + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + md = null; + } + + // If the member has a semtype component then it will be represented by that component otherwise with never. This + // means we depend on properly partitioning types to semtype components. Also, we need to ensure member types are + // "ready" when we call this + @Override + public SemType semType() { + if (md != null) { + return md.getSemType(env); + } + md = new MappingDefinition(); + if (hasTypeHoles()) { + return md.defineMappingTypeWrapped(env, List.of(), VAL); + } + SemType elementTypeSemType = constraint.semType(); + if (elementTypeSemType == null) { + elementTypeSemType = NEVER; + } + boolean isReadonly = Symbols.isFlagOn(getFlags(), Flags.READONLY); + CellAtomicType.CellMutability mut = isReadonly ? CELL_MUT_NONE : CELL_MUT_LIMITED; + return md.defineMappingTypeWrapped(env, List.of(), elementTypeSemType, mut); + } + + // This is to ensure call to isNullable won't call semType. In case this is a member of a recursive union otherwise + // this will have an invalid map type since parent union type call this while it is filling its members + @Override + public boolean isNullable() { + return false; + } + + @Override + public void setFlags(long flags) { + super.setFlags(flags); + restMd(); + } + + @Override + public void addFlags(long flags) { + super.addFlags(flags); + restMd(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNeverType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNeverType.java index fd43d8e3d06f..7dd18758dbfb 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNeverType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNeverType.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.PredefinedType; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -31,13 +32,8 @@ public class BNeverType extends BType { - public BNeverType() { - super(TypeTags.NEVER, null, Flags.READONLY); - } - - @Override - public boolean isNullable() { - return false; + protected BNeverType() { + super(TypeTags.NEVER, null, Flags.READONLY, PredefinedType.NEVER); } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNilType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNilType.java index 8abb31ab1370..0b821361a5c9 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNilType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNilType.java @@ -17,8 +17,7 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.types.NullType; -import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; +import io.ballerina.types.PredefinedType; import org.wso2.ballerinalang.compiler.util.Names; import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; @@ -29,24 +28,14 @@ * * @since 0.970.0 */ -public class BNilType extends BType implements NullType { +public class BNilType extends BType { - public BNilType() { - super(TypeTags.NIL, null, Flags.READONLY); - } - - @Override - public boolean isNullable() { - return true; + BNilType() { + super(TypeTags.NIL, null, Flags.READONLY, PredefinedType.NIL); } @Override public String toString() { return Names.NIL_VALUE.value; } - - @Override - public void accept(TypeVisitor visitor) { - visitor.visit(this); - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNoType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNoType.java index 3a4abd20fde8..f3a95d660c68 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNoType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BNoType.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.types.NoType; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -26,7 +27,7 @@ public class BNoType extends BType implements NoType { public BNoType(int tag) { - super(tag, null); + super(tag, null, PredefinedType.UNDEF); } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BObjectType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BObjectType.java index 8abd29cc8474..59ac8e697a79 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BObjectType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BObjectType.java @@ -17,6 +17,14 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.definition.Member; +import io.ballerina.types.definition.ObjectDefinition; +import io.ballerina.types.definition.ObjectQualifiers; import org.ballerinalang.model.types.ObjectType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -28,6 +36,17 @@ import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; + /** * {@code BObjectType} represents object type in Ballerina. * @@ -42,6 +61,7 @@ public class BObjectType extends BStructureType implements ObjectType { private static final String RIGHT_CURL = "}"; private static final String SEMI_COLON = ";"; private static final String READONLY = "readonly"; + private final Env env; public boolean markedIsolatedness; public BObjectType mutableType = null; @@ -49,12 +69,21 @@ public class BObjectType extends BStructureType implements ObjectType { public BTypeIdSet typeIdSet = new BTypeIdSet(); - public BObjectType(BTypeSymbol tSymbol) { + private ObjectDefinition od = null; + private final DistinctIdSupplier distinctIdSupplier; + + public BObjectType(Env env, BTypeSymbol tSymbol) { super(TypeTags.OBJECT, tSymbol); + assert env != null; + this.env = env; + this.distinctIdSupplier = new DistinctIdSupplier(env); } - public BObjectType(BTypeSymbol tSymbol, long flags) { + public BObjectType(Env env, BTypeSymbol tSymbol, long flags) { super(TypeTags.OBJECT, tSymbol, flags); + assert env != null; + this.env = env; + this.distinctIdSupplier = new DistinctIdSupplier(env); } @Override @@ -72,6 +101,104 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + private boolean hasTypeHoles() { + return fields.values().stream().anyMatch(field -> field.type instanceof BNoType); + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + od = null; + } + + @Override + public SemType semType() { + return distinctIdWrapper(semTypeInner()); + } + + SemType distinctIdWrapper(SemType semTypeInner) { + return distinctIdSupplier.get().stream().map(SemTypes::objectDistinct).reduce(semTypeInner, Core::intersect); + } + + private SemType semTypeInner() { + if (od != null) { + return od.getSemType(env); + } + od = new ObjectDefinition(); + // I don't think this is actually possible + assert !hasTypeHoles() : "unimplemented"; + List members = new ArrayList<>(fields.size()); + ObjectQualifiers qualifiers = getObjectQualifiers(); + Set memberNames = new HashSet<>(); + for (BField field : fields.values()) { + Optional member = createMember(field, qualifiers.readonly(), memberNames); + member.ifPresent(members::add); + } + + BObjectTypeSymbol objectSymbol = (BObjectTypeSymbol) this.tsymbol; + for (BAttachedFunction fun : objectSymbol.attachedFuncs) { + Optional member = createMember(fun, memberNames); + member.ifPresent(members::add); + } + return od.define(env, qualifiers, members); + } + + private static Optional createMember(BAttachedFunction func, Set visitedFields) { + String name = func.funcName.value; + if (Symbols.isFlagOn(func.symbol.flags, Flags.REMOTE)) { + name = "$remote$" + name; + } + if (visitedFields.contains(name)) { + return Optional.empty(); + } + visitedFields.add(name); + Member.Visibility visibility = Symbols.isFlagOn(func.symbol.flags, Flags.PUBLIC) ? + Member.Visibility.Public : Member.Visibility.Private; + SemType type = func.semType(); + assert type != null : "function type is fully implemented"; + assert !Core.isNever(type) : "method can't be never"; + return Optional.of(new Member(name, type, Member.Kind.Method, visibility, true)); + } + + private static Optional createMember(BField field, boolean readonlyObject, Set visitedFields) { + String name = field.name.value; + if (visitedFields.contains(name)) { + return Optional.empty(); + } + visitedFields.add(name); + Member.Visibility visibility = Symbols.isFlagOn(field.symbol.flags, Flags.PUBLIC) ? + Member.Visibility.Public : Member.Visibility.Private; + SemType type = field.type.semType(); + if (type == null) { + type = PredefinedType.NEVER; + } + boolean immutableField; + if (readonlyObject || Symbols.isFlagOn(field.symbol.flags, Flags.READONLY)) { + type = Core.intersect(type, PredefinedType.VAL_READONLY); + immutableField = true; + } else { + immutableField = false; + } + return Optional.of(new Member(name, type, Member.Kind.Field, visibility, immutableField)); + } + + private ObjectQualifiers getObjectQualifiers() { + long flags = tsymbol.flags; + boolean isolated = Symbols.isFlagOn(this.tsymbol.flags, Flags.ISOLATED); + ObjectQualifiers.NetworkQualifier networkQualifier; + if (Symbols.isFlagOn(flags, Flags.SERVICE)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Service; + } else if (Symbols.isFlagOn(flags, Flags.CLIENT)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Client; + } else { + networkQualifier = ObjectQualifiers.NetworkQualifier.None; + } + boolean readonly = Symbols.isFlagOn(this.tsymbol.flags, Flags.READONLY); + return new ObjectQualifiers(isolated, readonly, networkQualifier); + } + @Override public String toString() { if (shouldPrintShape()) { @@ -119,4 +246,35 @@ public String toString() { } return this.tsymbol.toString(); } + + // This is to ensure call to isNullable won't call semType. In case this is a member of a recursive union otherwise + // this will have an invalid object type since parent union type call this while it is filling its members + @Override + public boolean isNullable() { + return false; + } + + private final class DistinctIdSupplier implements Supplier> { + + private List ids = null; + private static final Map> allocatedIds = + Collections.synchronizedMap(new WeakHashMap<>()); + private final Env env; + + private DistinctIdSupplier(Env env) { + this.env = env; + allocatedIds.putIfAbsent(env, new ConcurrentHashMap<>()); + } + + public synchronized List get() { + if (ids != null) { + return ids; + } + Map envAllocatedIds = allocatedIds.get(env); + ids = typeIdSet.getAll().stream() + .map(each -> envAllocatedIds.computeIfAbsent(each, (key) -> env.distinctAtomCountGetAndIncrement())) + .toList(); + return ids; + } + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BParameterizedType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BParameterizedType.java index f966d588da46..b4aa3a60a095 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BParameterizedType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BParameterizedType.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.SemType; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BVarSymbol; @@ -45,11 +46,6 @@ public BParameterizedType(BType valueType, BVarSymbol paramSymbol, BTypeSymbol t this.paramIndex = paramIndex; } - @Override - public boolean isNullable() { - return false; - } - @Override public String toString() { return this.paramSymbol.name.toString(); @@ -64,4 +60,9 @@ public void accept(TypeVisitor visitor) { public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + + @Override + public SemType semType() { + return this.paramValueType.semType(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BReadonlyType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BReadonlyType.java index b59d62a41f4a..eab400866809 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BReadonlyType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BReadonlyType.java @@ -17,36 +17,40 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.Name; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; import org.ballerinalang.model.types.TypeKind; -import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import static io.ballerina.types.PredefinedType.VAL_READONLY; + /** * {@code BReadonlyType} represents the shapes that have their read-only bit on. * * @since 1.3.0 */ -public class BReadonlyType extends BBuiltInRefType { +public class BReadonlyType extends BType { private boolean nullable = true; - public BReadonlyType(int tag, BTypeSymbol tsymbol) { - super(tag, tsymbol); - this.flags |= Flags.READONLY; + public BReadonlyType() { + this(VAL_READONLY); + } + + public BReadonlyType(long flag) { + super(TypeTags.READONLY, null, flag | Flags.READONLY, VAL_READONLY); } - public BReadonlyType(int tag, BTypeSymbol tsymbol, Name name, long flag) { - super(tag, tsymbol); - this.name = name; - this.flags = flag; - this.flags |= Flags.READONLY; + private BReadonlyType(SemType semType) { + super(TypeTags.READONLY, null, Flags.READONLY, semType); } - public BReadonlyType(int tag, BTypeSymbol tsymbol, boolean nullable) { - super(tag, tsymbol); - this.nullable = nullable; - this.flags |= Flags.READONLY; + public static BReadonlyType newNilLiftedBReadonlyType() { + BReadonlyType result = new BReadonlyType(Core.diff(VAL_READONLY, PredefinedType.NIL)); + result.nullable = false; + return result; } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRecordType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRecordType.java index a6933f7e6856..95200e5c6a57 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRecordType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRecordType.java @@ -17,6 +17,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.Field; +import io.ballerina.types.definition.MappingDefinition; import org.ballerinalang.model.types.RecordType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -25,6 +31,14 @@ import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; +import java.util.ArrayList; +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.VAL; + /** * {@code BRecordType} represents record type in Ballerina. * @@ -42,16 +56,28 @@ public class BRecordType extends BStructureType implements RecordType { public static final String READONLY = "readonly"; public boolean sealed; public BType restFieldType; - public Boolean isAnyData = null; public BRecordType mutableType; - public BRecordType(BTypeSymbol tSymbol) { + private final Env env; + private MappingDefinition md = null; + + public BRecordType(Env env, BTypeSymbol tSymbol) { super(TypeTags.RECORD, tSymbol); + this.env = env; } - public BRecordType(BTypeSymbol tSymbol, long flags) { + public BRecordType(Env env, BTypeSymbol tSymbol, long flags) { super(TypeTags.RECORD, tSymbol, flags); + this.env = env; + } + + /** + * It is required to reset {@link #md} when the type gets mutated. + * This method is used for that. e.g. When changing Flags.READONLY + */ + protected void restMd() { + md = null; } @Override @@ -95,12 +121,102 @@ public String toString() { } if (sealed) { sb.append(SPACE).append(CLOSE_RIGHT); - return !Symbols.isFlagOn(this.flags, Flags.READONLY) ? sb.toString() : + return !Symbols.isFlagOn(this.getFlags(), Flags.READONLY) ? sb.toString() : sb.toString().concat(" & readonly"); } sb.append(SPACE).append(restFieldType).append(REST).append(SEMI).append(SPACE).append(CLOSE_RIGHT); - return !Symbols.isFlagOn(this.flags, Flags.READONLY) ? sb.toString() : sb.toString().concat(" & readonly"); + return !Symbols.isFlagOn(this.getFlags(), Flags.READONLY) ? sb.toString() : + sb.toString().concat(" & readonly"); } return this.tsymbol.toString(); } + + private boolean hasTypeHoles() { + if (this.fields != null) { + for (BField member : this.fields.values()) { + if (member.type instanceof BNoType) { + return true; + } + } + } + + // Note: restFieldType will be null/BNoType for closed records + return false; + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + md = null; + } + + // If the member has a semtype component then it will be represented by that component otherwise with never. This + // means we depend on properly partitioning types to semtype components. Also, we need to ensure member types are + // "ready" when we call this + @Override + public SemType semType() { + if (md != null) { + return md.getSemType(env); + } + md = new MappingDefinition(); + if (hasTypeHoles()) { + return md.defineMappingTypeWrapped(env, List.of(), VAL); + } + + SemType restFieldSemType; + if (restFieldType == null || restFieldType instanceof BNoType || restFieldType.semType() == null) { + restFieldSemType = NEVER; + } else { + restFieldSemType = restFieldType.semType(); + } + + List semFields = new ArrayList<>(this.fields.size()); + for (BField field : this.fields.values()) { + boolean optional = Symbols.isOptional(field.symbol); + BType bType = field.type; + SemType ty = bType.semType(); + if (ty == null || NEVER.equals(ty)) { + if (!optional) { + // if there is a non-optional field with `never` type, it is not possible to create a value. + // Hence, the whole record type is considered as `never`. + md.setSemTypeToNever(); + return NEVER; + } + + if (Core.isNever(restFieldSemType)) { + // record { never x?; never...;} is equivalent to record { never... } + // ignore the field + continue; + } + } + Field semField = Field.from(field.name.value, ty, Symbols.isFlagOn(field.symbol.flags, Flags.READONLY), + optional); + semFields.add(semField); + } + + boolean isReadonly = Symbols.isFlagOn(getFlags(), Flags.READONLY); + CellAtomicType.CellMutability mut = isReadonly ? CELL_MUT_NONE : CELL_MUT_LIMITED; + return md.defineMappingTypeWrapped(env, semFields, restFieldSemType, mut); + } + + // This is to ensure call to isNullable won't call semType. In case this is a member of a recursive union otherwise + // this will have an invalid record type since parent union type call this while it is filling its members + @Override + public boolean isNullable() { + return false; + } + + @Override + public void setFlags(long flags) { + super.setFlags(flags); + restMd(); + } + + @Override + public void addFlags(long flags) { + super.addFlags(flags); + restMd(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRegexpType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRegexpType.java index 900216e6fef0..16f005bc8d06 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRegexpType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BRegexpType.java @@ -17,10 +17,11 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.Name; +import io.ballerina.types.PredefinedType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.util.Names; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; /** @@ -30,13 +31,8 @@ */ public class BRegexpType extends BType { - public BRegexpType(int tag, Name name) { - super(tag, null, name, Flags.READONLY); - } - - @Override - public boolean isNullable() { - return false; + public BRegexpType() { + super(TypeTags.REGEXP, null, Names.REGEXP_TYPE, Flags.READONLY, PredefinedType.REGEXP); } @Override @@ -64,12 +60,4 @@ public String getQualifiedTypeName() { return Names.BALLERINA_ORG.value + Names.ORG_NAME_SEPARATOR.value + Names.LANG.value + Names.DOT.value + Names.REGEXP.value + Names.ALIAS_SEPARATOR + name; } - - public boolean isAnydata() { - return true; - } - - public boolean isPureType() { - return true; - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BSequenceType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BSequenceType.java index fee674386469..571887ad0c55 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BSequenceType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BSequenceType.java @@ -17,8 +17,15 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Env; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.ListDefinition; import org.wso2.ballerinalang.compiler.util.TypeTags; +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; + /** * Represents type for sequence variable. * @@ -26,13 +33,25 @@ */ public class BSequenceType extends BType { public BType elementType; - public BSequenceType(BType elementType) { + private final Env env; + + public BSequenceType(Env env, BType elementType) { super(TypeTags.SEQUENCE, null); this.elementType = elementType; + this.env = env; } @Override public String toString() { return "seq " + elementType; } + + public SemType semType() { + if (this.semType != null) { + return this.semType; + } + ListDefinition ld = new ListDefinition(); + this.semType = ld.defineListTypeWrapped(env, List.of(), 0, elementType.semType(), CELL_MUT_LIMITED); + return this.semType; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStreamType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStreamType.java index e71f6997b6f1..5f218cf9a3d6 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStreamType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStreamType.java @@ -18,8 +18,13 @@ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.StreamDefinition; import org.ballerinalang.model.types.StreamType; import org.ballerinalang.model.types.Type; +import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -29,15 +34,19 @@ * * @since 1.2.0 */ -public class BStreamType extends BBuiltInRefType implements StreamType { +public class BStreamType extends BType implements StreamType { public BType constraint; public BType completionType; - public BStreamType(int tag, BType constraint, BType completionType, BTypeSymbol tsymbol) { + private final Env env; + private StreamDefinition d = null; + + public BStreamType(Env env, int tag, BType constraint, BType completionType, BTypeSymbol tsymbol) { super(tag, tsymbol); this.constraint = constraint; - this.completionType = completionType != null ? completionType : new BNilType(); + this.completionType = completionType != null ? completionType : BType.createNilType(); + this.env = env; } @Override @@ -55,6 +64,11 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + @Override + public TypeKind getKind() { + return TypeKind.STREAM; + } + @Override public String toString() { if (constraint.tag == TypeTags.ANY) { @@ -69,4 +83,26 @@ public String toString() { public void accept(TypeVisitor visitor) { visitor.visit(this); } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + d = null; + } + + @Override + public SemType semType() { + if (constraint == null || constraint instanceof BNoType) { + return PredefinedType.STREAM; + } + + if (d != null) { + return d.getSemType(env); + } + + d = new StreamDefinition(); + return d.define(env, constraint.semType(), completionType.semType()); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStringSubType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStringSubType.java index d63c962f96d9..af7beacd9ec3 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStringSubType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BStringSubType.java @@ -17,10 +17,11 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.Name; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.util.Names; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; /** @@ -30,55 +31,40 @@ */ public class BStringSubType extends BType { - public BStringSubType(int tag, Name name) { + public static final BStringSubType CHAR = new BStringSubType(); - super(tag, null, name, Flags.READONLY); + private BStringSubType() { + super(TypeTags.CHAR_STRING, null, Names.CHAR, Flags.READONLY, SemTypes.CHAR); } @Override public boolean isNullable() { - return false; } @Override public R accept(BTypeVisitor visitor, T t) { - return visitor.visit(this, t); } @Override public TypeKind getKind() { - return TypeKind.STRING; } @Override public void accept(TypeVisitor visitor) { - visitor.visit(this); } @Override public String toString() { - return Names.STRING.value + Names.ALIAS_SEPARATOR + name; } @Override public String getQualifiedTypeName() { - return Names.BALLERINA_ORG.value + Names.ORG_NAME_SEPARATOR.value + Names.LANG.value + Names.DOT.value + Names.STRING.value + Names.ALIAS_SEPARATOR + name; } - - public boolean isAnydata() { - - return true; - } - - public boolean isPureType() { - - return true; - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTableType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTableType.java index d4c3ce7fea6d..f5bb090449b5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTableType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTableType.java @@ -15,15 +15,21 @@ * specific language governing permissions and limitations * under the License. */ - package org.wso2.ballerinalang.compiler.semantics.model.types; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Context; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.TableType; import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.semantics.analyzer.Types; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; +import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; import java.util.ArrayList; @@ -43,21 +49,29 @@ public class BTableType extends BType implements TableType { public Location constraintPos; public BTableType mutableType; + private final Env env; - public BTableType(int tag, BType constraint, BTypeSymbol tSymbol) { - super(tag, tSymbol); + public BTableType(Env env, BType constraint, BTypeSymbol tSymbol) { + super(TypeTags.TABLE, tSymbol); this.constraint = constraint; + this.env = env; } - public BTableType(int tag, BType constraint, BTypeSymbol tSymbol, long flags) { - super(tag, tSymbol, flags); + public BTableType(Env env, BType constraint, BTypeSymbol tSymbol, long flags) { + super(TypeTags.TABLE, tSymbol, flags); this.constraint = constraint; + this.env = env; } public BType getConstraint() { return this.constraint; } + public void setConstraint(BType constraint) { + this.constraint = constraint; + this.semType = null; + } + @Override public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); @@ -65,7 +79,7 @@ public R accept(BTypeVisitor visitor, T t) { @Override public String toString() { - boolean readonly = Symbols.isFlagOn(flags, Flags.READONLY); + boolean readonly = Symbols.isFlagOn(getFlags(), Flags.READONLY); if (constraint == null) { return readonly ? super.toString().concat(" & readonly") : super.toString(); } @@ -97,4 +111,50 @@ public TypeKind getKind() { public void accept(TypeVisitor visitor) { visitor.visit(this); } + + @Override + public SemType semType() { + if (this.semType != null) { + return this.semType; + } + + SemType s = semTypeInner(); + boolean readonly = Symbols.isFlagOn(this.getFlags(), Flags.READONLY); + this.semType = readonly ? SemTypes.intersect(PredefinedType.VAL_READONLY, s) : s; + return this.semType; + } + + private SemType semTypeInner() { + BType constraintType = Types.getReferredType(constraint); + if (constraintType.tag == TypeTags.TABLE || constraintType.tag == TypeTags.SEMANTIC_ERROR) { + // this is to handle negative table recursions. e.g. type T table + return PredefinedType.TABLE; + } else if (constraintType instanceof BIntersectionType intersectionType) { + for (BType memberType : intersectionType.getConstituentTypes()) { + if (Types.getReferredType(memberType).tag == TypeTags.TABLE) { + // Negative scenario + return PredefinedType.TABLE; + } + } + } + + SemType tableConstraint = constraintType instanceof BParameterizedType p ? p.paramValueType.semType() : + constraintType.semType(); + tableConstraint = SemTypes.intersect(tableConstraint, PredefinedType.MAPPING); + + Context cx = Context.from(env); // apis calling with 'cx' here are only accessing the env field internally + String[] fieldNames = fieldNameList.toArray(String[]::new); + if (!fieldNameList.isEmpty()) { + return SemTypes.tableContainingKeySpecifier(cx, tableConstraint, fieldNames); + } + + BType keyConstraintType = Types.getReferredType(keyTypeConstraint); + if (keyTypeConstraint != null && keyConstraintType.tag != TypeTags.NEVER && + keyTypeConstraint.tag != TypeTags.SEMANTIC_ERROR) { + SemType keyConstraint = keyTypeConstraint.semType(); + return SemTypes.tableContainingKeyConstraint(cx, tableConstraint, keyConstraint); + } + + return SemTypes.tableContaining(env, tableConstraint); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTupleType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTupleType.java index 6b647c1c2052..54c93ad0f200 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTupleType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTupleType.java @@ -16,6 +16,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.ListDefinition; import org.ballerinalang.model.types.TupleType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -26,8 +30,14 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.VAL; + /** * {@code {@link BTupleType }} represents the tuple type. * @@ -37,56 +47,71 @@ public class BTupleType extends BType implements TupleType { private List members; private List memberTypes; public BType restType; - public Boolean isAnyData = null; public boolean resolvingToString = false; public boolean isCyclic = false; public BTupleType mutableType; + private final Env env; + private ListDefinition ld = null; - public BTupleType(List members) { + public BTupleType(Env env, List members) { super(TypeTags.TUPLE, null); this.members = members; + this.env = env; } - public BTupleType(BTypeSymbol tsymbol, List members) { + public BTupleType(Env env, BTypeSymbol tsymbol, List members) { super(TypeTags.TUPLE, tsymbol); this.members = members; + this.env = env; } - public BTupleType(BTypeSymbol tsymbol, List members, boolean isCyclic) { + public BTupleType(Env env, BTypeSymbol tsymbol, List members, boolean isCyclic) { super(TypeTags.TUPLE, tsymbol); this.members = members; this.isCyclic = isCyclic; + this.env = env; } - public BTupleType(BTypeSymbol tsymbol, List members, BType restType, long flags) { + public BTupleType(Env env, BTypeSymbol tsymbol, List members, BType restType, long flags) { super(TypeTags.TUPLE, tsymbol, flags); this.members = members; this.restType = restType; + this.env = env; } - public BTupleType(BTypeSymbol tsymbol, List members, BType restType, long flags, + public BTupleType(Env env, BTypeSymbol tsymbol, List members, BType restType, long flags, boolean isCyclic) { super(TypeTags.TUPLE, tsymbol, flags); this.members = members; this.restType = restType; this.isCyclic = isCyclic; + this.env = env; } - public BTupleType(BTypeSymbol tsymbol) { - this(tsymbol, true); + public BTupleType(Env env, BTypeSymbol tsymbol) { + this(env, tsymbol, true); } - private BTupleType(BTypeSymbol tsymbol, boolean readonly) { + private BTupleType(Env env, BTypeSymbol tsymbol, boolean readonly) { super(TypeTags.TUPLE, tsymbol); if (readonly) { - this.flags |= Flags.READONLY; + this.addFlags(Flags.READONLY); if (tsymbol != null) { this.tsymbol.flags |= Flags.READONLY; } } + this.env = env; + } + + /** + * It is required to reset {@link #ld} when the type gets mutated. + * This method is used for that. e.g. When changing Flags.READONLY + */ + protected void restLd() { + ld = null; } @Override @@ -132,12 +157,13 @@ public String toString() { + ((restType != null) ? (!members.isEmpty() ? "," : "") + restType.toString() + "...]" : "]"); this.resolvingToString = false; - return !Symbols.isFlagOn(flags, Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); } // In the case of a cyclic tuple, this aids in //adding resolved members to a previously defined empty tuple shell in main scope public boolean addMembers(BTupleMember member) { + ld = null; // Prevent cyclic types of same type ex: type Foo [int, Foo]; if (member.type instanceof BTupleType && ((BTupleType) member.type).isCyclic && member.type.getQualifiedTypeName().equals(this.getQualifiedTypeName())) { @@ -147,8 +173,9 @@ public boolean addMembers(BTupleMember member) { if (this.memberTypes != null) { this.memberTypes.add(member.type); } - if (Symbols.isFlagOn(this.flags, Flags.READONLY) && !Symbols.isFlagOn(member.type.flags, Flags.READONLY)) { - this.flags ^= Flags.READONLY; + if (Symbols.isFlagOn(this.getFlags(), Flags.READONLY) && + !Symbols.isFlagOn(member.type.getFlags(), Flags.READONLY)) { + this.setFlags(this.getFlags() ^ Flags.READONLY); } setCyclicFlag(member.type); return true; @@ -158,13 +185,15 @@ public boolean addMembers(BTupleMember member) { // adding rest type of resolved node to a previously defined // empty tuple shell in main scope public boolean addRestType(BType restType) { + ld = null; if (restType != null && restType instanceof BTupleType && ((BTupleType) restType).isCyclic && restType.getQualifiedTypeName().equals(this.getQualifiedTypeName()) && this.members.isEmpty()) { return false; } this.restType = restType; - if (Symbols.isFlagOn(this.flags, Flags.READONLY) && !Symbols.isFlagOn(restType.flags, Flags.READONLY)) { - this.flags ^= Flags.READONLY; + if (Symbols.isFlagOn(this.getFlags(), Flags.READONLY) && + !Symbols.isFlagOn(restType.getFlags(), Flags.READONLY)) { + this.setFlags(this.getFlags() ^ Flags.READONLY); } setCyclicFlag(restType); return true; @@ -174,6 +203,7 @@ public void setMembers(List members) { assert members.isEmpty(); this.memberTypes = null; this.members = members; + ld = null; } private void setCyclicFlag(BType type) { @@ -205,4 +235,82 @@ private void setCyclicFlag(BType type) { } } } + + // This is to ensure call to isNullable won't call semType. In case this is a member of a recursive union otherwise + // this will have an invalid list type since parent union type call this while it is filling its members + @Override + public boolean isNullable() { + return false; + } + + private boolean hasTypeHoles() { + if (members != null) { + for (BTupleMember member : members) { + if (member.type instanceof BNoType) { + return true; + } + } + } + if (restType != null) { + return restType instanceof BNoType; + } + return false; + } + + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + ld = null; + } + + // If the member has a semtype component then it will be represented by that component otherwise with never. This + // means we depend on properly partitioning types to semtype components. Also, we need to ensure member types are + // "ready" when we call this + @Override + public SemType semType() { + if (ld != null) { + return ld.getSemType(env); + } + ld = new ListDefinition(); + if (hasTypeHoles()) { + return ld.defineListTypeWrapped(env, VAL); + } + boolean isReadonly = Symbols.isFlagOn(getFlags(), Flags.READONLY); + CellAtomicType.CellMutability mut = isReadonly ? CELL_MUT_NONE : CELL_MUT_LIMITED; + if (members == null) { + if (restType == null) { + throw new IllegalStateException("Both members and rest type can't be null"); + } + SemType restSemType = restType.semType(); + return ld.defineListTypeWrapped(env, List.of(), 0, Objects.requireNonNullElse(restSemType, NEVER), mut); + } + List memberSemTypes = new ArrayList<>(members.size()); + for (BTupleMember member : members) { + BType memberType = member.type; + SemType semType = memberType.semType(); + if (semType == null) { + semType = NEVER; + } + memberSemTypes.add(semType); + } + SemType restSemType = restType != null ? restType.semType() : NEVER; + if (restSemType == null) { + restSemType = NEVER; + } + return ld.defineListTypeWrapped(env, memberSemTypes, memberSemTypes.size(), restSemType, mut); + } + + @Override + public void setFlags(long flags) { + super.setFlags(flags); + restLd(); + } + + @Override + public void addFlags(long flags) { + super.addFlags(flags); + restLd(); + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BType.java index a60272975151..3704a586bb7b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BType.java @@ -17,6 +17,8 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.SemType; import org.ballerinalang.model.Name; import org.ballerinalang.model.types.TypeKind; import org.ballerinalang.model.types.ValueType; @@ -51,27 +53,52 @@ public class BType implements ValueType { // sometimes we loose type param information down the line. which is a problem. // TODO: Refactor this after JBallerina 1.0. public Name name; - public long flags; + private long flags; + + protected SemType semType; public BType(int tag, BTypeSymbol tsymbol) { - this.tag = tag; - this.tsymbol = tsymbol; - this.name = Names.EMPTY; - this.flags = 0; + this(tag, tsymbol, Names.EMPTY, 0, null); + } + + public BType(int tag, BTypeSymbol tsymbol, SemType semType) { + this(tag, tsymbol, Names.EMPTY, 0, semType); } public BType(int tag, BTypeSymbol tsymbol, long flags) { - this.tag = tag; - this.tsymbol = tsymbol; - this.name = Names.EMPTY; - this.flags = flags; + this(tag, tsymbol, Names.EMPTY, flags, null); } public BType(int tag, BTypeSymbol tsymbol, Name name, long flags) { + this(tag, tsymbol, name, flags, null); + } + + public BType(int tag, BTypeSymbol tsymbol, long flags, SemType semType) { + this(tag, tsymbol, Names.EMPTY, flags, semType); + } + + public BType(int tag, BTypeSymbol tsymbol, Name name, long flags, SemType semType) { this.tag = tag; this.tsymbol = tsymbol; this.name = name; this.flags = flags; + this.semType = semType; + } + + public static BType createNilType() { + return new BNilType(); + } + + public static BType createNeverType() { + return new BNeverType(); + } + + public SemType semType() { + return semType; + } + + public void semType(SemType semtype) { + this.semType = semtype; } public BType getReturnType() { @@ -79,7 +106,7 @@ public BType getReturnType() { } public boolean isNullable() { - return false; + return Core.containsNil(semType()); } public R accept(BTypeVisitor visitor, T t) { @@ -119,6 +146,25 @@ public String getQualifiedTypeName() { return tsymbol.pkgID.toString() + ":" + tsymbol.name; } + public final long getFlags() { + return flags; + } + + public void setFlags(long flags) { + this.flags = flags; + } + + public void addFlags(long flags) { + this.flags |= flags; + this.resetSemType(); + } + + /** + * When the type is mutated we need to reset resolved semType. + */ + public void resetSemType() { + } + /** * A data holder to hold the type associated with an expression. */ diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeReferenceType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeReferenceType.java index 4914628e9a6d..91edd117538c 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeReferenceType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeReferenceType.java @@ -17,7 +17,7 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; -import org.ballerinalang.model.types.ReferenceType; +import io.ballerina.types.SemType; import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; @@ -27,11 +27,10 @@ /** * @since 2.0.0 */ -public class BTypeReferenceType extends BType implements ReferenceType { +public class BTypeReferenceType extends BType { public BType referredType; public final String definitionName; - private Boolean nilable = null; public BTypeReferenceType(BType referredType, BTypeSymbol tsymbol, long flags) { super(TYPEREFDESC, tsymbol, flags); @@ -39,9 +38,9 @@ public BTypeReferenceType(BType referredType, BTypeSymbol tsymbol, long flags) { this.definitionName = tsymbol.getName().getValue(); } - public BTypeReferenceType(BType referredType, BTypeSymbol tsymbol, long flags, boolean nilable) { - this(referredType, tsymbol, flags); - this.nilable = nilable; + @Override + public SemType semType() { + return referredType.semType(); } @Override @@ -64,14 +63,4 @@ public void accept(TypeVisitor visitor) { public TypeKind getKind() { return TypeKind.TYPEREFDESC; } - - @Override - public boolean isNullable() { - if (this.nilable != null) { - return this.nilable; - } - - this.nilable = this.referredType.isNullable(); - return this.nilable; - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeVisitor.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeVisitor.java index fa8d92c78151..ac742e46f36d 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeVisitor.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypeVisitor.java @@ -25,8 +25,6 @@ public interface BTypeVisitor { R visit(BType t, T s); - R visit(BBuiltInRefType t, T s); - R visit(BAnyType t, T s); R visit(BAnydataType t, T s); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypedescType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypedescType.java index cdd883b5b4e5..5c92251bfe7b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypedescType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BTypedescType.java @@ -17,7 +17,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.ConstrainedType; +import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -26,31 +31,39 @@ /** * @since 0.94 */ -public class BTypedescType extends BBuiltInRefType implements ConstrainedType { +public class BTypedescType extends BType implements ConstrainedType { public BType constraint; + private final Env env; - public BTypedescType(BType constraint, BTypeSymbol tsymbol) { - + public BTypedescType(Env env, BType constraint, BTypeSymbol tsymbol) { super(TypeTags.TYPEDESC, tsymbol, Flags.READONLY); this.constraint = constraint; + this.env = env; + } + + public BTypedescType(Env env, BType constraint, BTypeSymbol tsymbol, SemType semType) { + this(env, constraint, tsymbol); + this.semType = semType; } @Override public BType getConstraint() { - return constraint; } @Override public R accept(BTypeVisitor visitor, T t) { - return visitor.visit(this, t); } @Override - public String toString() { + public TypeKind getKind() { + return TypeKind.TYPEDESC; + } + @Override + public String toString() { if (constraint.tag == TypeTags.ANY) { return super.toString(); } @@ -60,7 +73,20 @@ public String toString() { @Override public void accept(TypeVisitor visitor) { - visitor.visit(this); } + + @Override + public SemType semType() { + if (this.semType != null) { + return this.semType; + } + + if (constraint == null || constraint instanceof BNoType) { + this.semType = PredefinedType.TYPEDESC; + } else { + this.semType = SemTypes.typedescContaining(env, constraint.semType()); + } + return this.semType; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BUnionType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BUnionType.java index b73167676809..d414330d321b 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BUnionType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BUnionType.java @@ -17,6 +17,10 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.TypeKind; import org.ballerinalang.model.types.UnionType; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; @@ -44,14 +48,11 @@ */ public class BUnionType extends BType implements UnionType { public boolean resolvingToString = false; - private boolean nullable; private String cachedToString; protected LinkedHashSet memberTypes; - public Boolean isAnyData = null; - public Boolean isPureType = null; - public boolean isCyclic = false; + public boolean isCyclic = false; private LinkedHashSet originalMemberTypes; private static final String INT_CLONEABLE = "__Cloneable"; @@ -59,44 +60,38 @@ public class BUnionType extends BType implements UnionType { private static final String CLONEABLE_TYPE = "CloneableType"; private static final Pattern pCloneable = Pattern.compile(INT_CLONEABLE); private static final Pattern pCloneableType = Pattern.compile(CLONEABLE_TYPE); + public final Env env; - public BUnionType(BTypeSymbol tsymbol, LinkedHashSet memberTypes, boolean nullable, boolean readonly) { - this(tsymbol, memberTypes, memberTypes, nullable, readonly); + public BUnionType(Env env, BTypeSymbol tsymbol, LinkedHashSet memberTypes, boolean readonly) { + this(env, tsymbol, memberTypes, memberTypes, readonly, false); } - private BUnionType(BTypeSymbol tsymbol, LinkedHashSet originalMemberTypes, LinkedHashSet memberTypes, - boolean nullable, boolean readonly) { - super(TypeTags.UNION, tsymbol); - - if (readonly) { - this.flags |= Flags.READONLY; - - if (tsymbol != null) { - this.tsymbol.flags |= Flags.READONLY; - } - } + private BUnionType(Env env, BTypeSymbol tsymbol, LinkedHashSet originalMemberTypes, + LinkedHashSet memberTypes, boolean readonly) { + this(env, tsymbol, originalMemberTypes, memberTypes, readonly, false); + } - this.originalMemberTypes = originalMemberTypes; - this.memberTypes = memberTypes; - this.nullable = nullable; + private BUnionType(Env env, BTypeSymbol tsymbol, LinkedHashSet memberTypes, + boolean readonly, boolean isCyclic) { + this(env, tsymbol, null, memberTypes, readonly, isCyclic); } - private BUnionType(BTypeSymbol tsymbol, LinkedHashSet memberTypes, boolean nullable, boolean readonly, - boolean isCyclic) { + private BUnionType(Env env, BTypeSymbol tsymbol, LinkedHashSet originalMemberTypes, + LinkedHashSet memberTypes, boolean readonly, boolean isCyclic) { super(TypeTags.UNION, tsymbol); if (readonly) { - this.flags |= Flags.READONLY; + this.addFlags(Flags.READONLY); if (tsymbol != null) { this.tsymbol.flags |= Flags.READONLY; } } - this.originalMemberTypes = memberTypes; + this.originalMemberTypes = originalMemberTypes; this.memberTypes = memberTypes; - this.nullable = nullable; this.isCyclic = isCyclic; + this.env = env; } @Override @@ -129,11 +124,6 @@ public void accept(TypeVisitor visitor) { visitor.visit(this); } - @Override - public boolean isNullable() { - return nullable; - } - @Override public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); @@ -150,38 +140,35 @@ public String toString() { return cachedToString; } - public void setNullable(boolean nullable) { - this.nullable = nullable; - } - /** * Creates an empty union for cyclic union types. * + * @param env The environment to be used to create the union type. * @param tsymbol Type symbol for the union. * @param types The types to be used to define the union. * @param isCyclic The cyclic indicator. * @return The created union type. */ - public static BUnionType create(BTypeSymbol tsymbol, LinkedHashSet types, boolean isCyclic) { + public static BUnionType create(Env env, BTypeSymbol tsymbol, LinkedHashSet types, boolean isCyclic) { LinkedHashSet memberTypes = new LinkedHashSet<>(types.size()); boolean isImmutable = true; - boolean hasNilableType = false; - return new BUnionType(tsymbol, memberTypes, hasNilableType, isImmutable, isCyclic); + return new BUnionType(env, tsymbol, memberTypes, isImmutable, isCyclic); } /** * Creates a union type using the types specified in the `types` set. The created union will not have union types in * its member types set. If the set contains the nil type, calling isNullable() will return true. * + * @param env The environment to be used to create the union type. * @param tsymbol Type symbol for the union. * @param types The types to be used to define the union. * @return The created union type. */ - public static BUnionType create(BTypeSymbol tsymbol, LinkedHashSet types) { + public static BUnionType create(Env env, BTypeSymbol tsymbol, LinkedHashSet types) { LinkedHashSet memberTypes = new LinkedHashSet<>(types.size()); if (types.isEmpty()) { - return new BUnionType(tsymbol, memberTypes, false, true); + return new BUnionType(env, tsymbol, memberTypes, true); } boolean isImmutable = true; @@ -190,54 +177,32 @@ public static BUnionType create(BTypeSymbol tsymbol, LinkedHashSet types) memberTypes.add(memBType); } - if (isImmutable && !Symbols.isFlagOn(memBType.flags, Flags.READONLY)) { + if (isImmutable && !Symbols.isFlagOn(memBType.getFlags(), Flags.READONLY)) { isImmutable = false; } } - if (memberTypes.isEmpty()) { - memberTypes.add(new BNeverType()); - return new BUnionType(tsymbol, memberTypes, false, isImmutable); - } - boolean hasNilableType = false; - for (BType memberType : memberTypes) { - if (memberType.isNullable() && memberType.tag != TypeTags.NIL) { - hasNilableType = true; - break; - } - } - - if (hasNilableType) { - LinkedHashSet bTypes = new LinkedHashSet<>(memberTypes.size()); - for (BType t : memberTypes) { - if (t.tag != TypeTags.NIL) { - bTypes.add(t); - } - } - memberTypes = bTypes; - } - - for (BType memberType : memberTypes) { - if (memberType.isNullable()) { - return new BUnionType(tsymbol, types, memberTypes, true, isImmutable); - } + if (memberTypes.isEmpty()) { + memberTypes.add(BType.createNeverType()); + return new BUnionType(env, tsymbol, memberTypes, isImmutable); } - return new BUnionType(tsymbol, types, memberTypes, false, isImmutable); + return new BUnionType(env, tsymbol, types, memberTypes, isImmutable); } /** * Creates a union type using the provided types. If the set contains the nil type, calling isNullable() will return * true. * + * @param env The environment to be used to create the union type. * @param tsymbol Type symbol for the union. * @param types The types to be used to define the union. * @return The created union type. */ - public static BUnionType create(BTypeSymbol tsymbol, BType... types) { + public static BUnionType create(Env env, BTypeSymbol tsymbol, BType... types) { LinkedHashSet memberTypes = new LinkedHashSet<>(types.length); memberTypes.addAll(Arrays.asList(types)); - return create(tsymbol, memberTypes); + return create(env, tsymbol, memberTypes); } /** @@ -262,13 +227,12 @@ public void add(BType type) { this.memberTypes.add(type); } - if (Symbols.isFlagOn(this.flags, Flags.READONLY) && !Symbols.isFlagOn(type.flags, Flags.READONLY)) { - this.flags ^= Flags.READONLY; + if (Symbols.isFlagOn(this.getFlags(), Flags.READONLY) && !Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { + this.setFlags(this.getFlags() ^ Flags.READONLY); } setCyclicFlag(type); - - this.nullable = this.nullable || type.isNullable(); + this.semType = null; // reset cached sem-type if exists } private void setCyclicFlag(BType type) { @@ -319,24 +283,20 @@ public void remove(BType type) { } this.originalMemberTypes.remove(type); - if (type.isNullable()) { - this.nullable = false; - } - - if (Symbols.isFlagOn(this.flags, Flags.READONLY)) { + if (Symbols.isFlagOn(this.getFlags(), Flags.READONLY)) { return; } boolean isImmutable = true; for (BType memBType : this.memberTypes) { - if (!Symbols.isFlagOn(memBType.flags, Flags.READONLY)) { + if (!Symbols.isFlagOn(memBType.getFlags(), Flags.READONLY)) { isImmutable = false; break; } } if (isImmutable) { - this.flags |= Flags.READONLY; + this.addFlags(Flags.READONLY); } } @@ -351,28 +311,28 @@ public void mergeUnionType(BUnionType unionType) { for (BType member : unionType.getMemberTypes()) { if (member instanceof BArrayType arrayType) { if (getImpliedType(arrayType.eType) == unionType) { - BArrayType newArrayType = new BArrayType(this, arrayType.tsymbol, arrayType.size, - arrayType.state, arrayType.flags); + BArrayType newArrayType = new BArrayType(env, this, arrayType.tsymbol, arrayType.getSize(), + arrayType.state, arrayType.getFlags()); this.add(newArrayType); continue; } } else if (member instanceof BMapType mapType) { if (getImpliedType(mapType.constraint) == unionType) { - BMapType newMapType = new BMapType(mapType.tag, this, mapType.tsymbol, mapType.flags); + BMapType newMapType = new BMapType(env, mapType.tag, this, mapType.tsymbol, mapType.getFlags()); this.add(newMapType); continue; } } else if (member instanceof BTableType tableType) { if (getImpliedType(tableType.constraint) == unionType) { - BTableType newTableType = new BTableType(tableType.tag, this, tableType.tsymbol, - tableType.flags); + BTableType newTableType = new BTableType(env, this, tableType.tsymbol, + tableType.getFlags()); this.add(newTableType); continue; } else if (tableType.constraint instanceof BMapType mapType) { if (getImpliedType(mapType.constraint) == unionType) { - BMapType newMapType = new BMapType(mapType.tag, this, mapType.tsymbol, mapType.flags); - BTableType newTableType = new BTableType(tableType.tag, newMapType, tableType.tsymbol, - tableType.flags); + BMapType newMapType = new BMapType(env, mapType.tag, this, mapType.tsymbol, mapType.getFlags()); + BTableType newTableType = new BTableType(env, newMapType, tableType.tsymbol, + tableType.getFlags()); this.add(newTableType); continue; } @@ -445,7 +405,7 @@ private void computeStringRepresentation() { if (tsymbol != null && !tsymbol.getName().getValue().isEmpty()) { String typeName = tsymbol.getName().getValue(); String packageId = tsymbol.pkgID.toString(); - boolean isTypeParam = Symbols.isFlagOn(flags, Flags.TYPE_PARAM); + boolean isTypeParam = Symbols.isFlagOn(getFlags(), Flags.TYPE_PARAM); // improve readability of cyclic union types if (isCyclic && (pCloneable.matcher(typeName).matches() || (isTypeParam && pCloneableType.matcher(typeName).matches()))) { @@ -496,8 +456,8 @@ private void computeStringRepresentation() { String typeStr = numberOfNotNilTypes > 1 ? "(" + joiner + ")" : joiner.toString(); boolean hasNilType = uniqueTypes.size() > numberOfNotNilTypes; - cachedToString = (nullable && hasNilType && !hasNilableMember) ? (typeStr + Names.QUESTION_MARK.value) : - typeStr; + cachedToString = (this.isNullable() && hasNilType && !hasNilableMember) ? (typeStr + Names.QUESTION_MARK.value) + : typeStr; } private static boolean isNeverType(BType type) { @@ -515,4 +475,27 @@ private static boolean isNeverType(BType type) { } return false; } + + /** + * When the type is mutated we need to reset resolved semType. + */ + public void resetSemType() { + this.semType = null; + } + + @Override + public SemType semType() { + if (this.semType == null) { + this.semType = computeSemTypeFromMemberTypes(); + } + return this.semType; + } + + private SemType computeSemTypeFromMemberTypes() { + SemType t = PredefinedType.NEVER; + for (BType ty : this.memberTypes) { + t = SemTypes.union(t, ty.semType()); + } + return t; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLSubType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLSubType.java index eb62806d81ff..c51c77304eec 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLSubType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLSubType.java @@ -17,11 +17,18 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.Name; import org.ballerinalang.model.types.SelectivelyImmutableReferenceType; import org.ballerinalang.model.types.TypeKind; -import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.util.Names; +import org.wso2.ballerinalang.compiler.util.TypeTags; +import org.wso2.ballerinalang.util.Flags; + +import static org.wso2.ballerinalang.compiler.semantics.analyzer.Types.AND_READONLY_SUFFIX; /** * Represents Builtin Subtype of integer. @@ -29,54 +36,53 @@ * @since 1.2.0 */ public class BXMLSubType extends BType implements SelectivelyImmutableReferenceType { - public BXMLSubType(int tag, Name name) { - super(tag, null, name, 0); + public static final BXMLSubType XML_ELEMENT = new BXMLSubType(TypeTags.XML_ELEMENT, Names.XML_ELEMENT, + SemTypes.XML_ELEMENT); + public static final BXMLSubType XML_PI = new BXMLSubType(TypeTags.XML_PI, Names.XML_PI, SemTypes.XML_PI); + public static final BXMLSubType XML_COMMENT = new BXMLSubType(TypeTags.XML_COMMENT, Names.XML_COMMENT, + SemTypes.XML_COMMENT); + public static final BXMLSubType XML_TEXT = new BXMLSubType(TypeTags.XML_TEXT, Names.XML_TEXT, Flags.READONLY, + SemTypes.XML_TEXT); + + private BXMLSubType(int tag, Name name, SemType semType) { + this(tag, name, 0, semType); } - public BXMLSubType(int tag, Name name, long flags) { + private BXMLSubType(int tag, Name name, long flags, SemType semType) { + super(tag, null, name, flags, semType); + } - super(tag, null, name, flags); + public static BXMLSubType newImmutableXMLSubType(BXMLSubType xmlSubType) { + return new BXMLSubType(xmlSubType.tag, + Names.fromString(xmlSubType.name.getValue().concat(AND_READONLY_SUFFIX)), + xmlSubType.getFlags() | Flags.READONLY, + Core.intersect(xmlSubType.semType, PredefinedType.VAL_READONLY)); } @Override public boolean isNullable() { - return false; } @Override public R accept(BTypeVisitor visitor, T t) { - return visitor.visit(this, t); } @Override public TypeKind getKind() { - return TypeKind.XML; } - @Override - public void accept(TypeVisitor visitor) { - - visitor.visit(this); - } - @Override public String toString() { - return Names.XML.value + Names.ALIAS_SEPARATOR + name; } @Override public String getQualifiedTypeName() { - return Names.BALLERINA_ORG.value + Names.ORG_NAME_SEPARATOR.value + Names.LANG.value + Names.DOT.value + Names.XML.value + Names.ALIAS_SEPARATOR + name; } - - public boolean isAnydata() { - return true; - } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLType.java index f86112006093..602c52bb28d0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BXMLType.java @@ -16,7 +16,12 @@ */ package org.wso2.ballerinalang.compiler.semantics.model.types; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.SelectivelyImmutableReferenceType; +import org.ballerinalang.model.types.TypeKind; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.Symbols; @@ -29,7 +34,7 @@ * * @since 0.961.0 */ -public class BXMLType extends BBuiltInRefType implements SelectivelyImmutableReferenceType { +public class BXMLType extends BType implements SelectivelyImmutableReferenceType { public BType constraint; public BXMLType mutableType; @@ -54,7 +59,7 @@ public String toString() { stringRep = Names.XML.value; } - return !Symbols.isFlagOn(flags, Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); + return !Symbols.isFlagOn(getFlags(), Flags.READONLY) ? stringRep : stringRep.concat(" & readonly"); } @Override @@ -62,8 +67,42 @@ public R accept(BTypeVisitor visitor, T t) { return visitor.visit(this, t); } + @Override + public TypeKind getKind() { + return TypeKind.XML; + } + @Override public void accept(TypeVisitor visitor) { visitor.visit(this); } + + @Override + public SemType semType() { + if (this.semType != null) { + return this.semType; + } + + SemType s; + if (constraint == null) { + s = PredefinedType.XML; + } else { + SemType contraintSemtype; + if (constraint instanceof BParameterizedType parameterizedType) { + contraintSemtype = parameterizedType.paramValueType.semType(); + } else { + contraintSemtype = constraint.semType(); + } + + if (contraintSemtype == null || !Core.isSubtypeSimple(contraintSemtype, PredefinedType.XML)) { + // we reach here for negative semantics + contraintSemtype = PredefinedType.NEVER; + } + s = SemTypes.xmlSequence(contraintSemtype); + } + + boolean readonly = Symbols.isFlagOn(this.getFlags(), Flags.READONLY); + this.semType = readonly ? SemTypes.intersect(PredefinedType.VAL_READONLY, s) : s; + return this.semType; + } } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/SemNamedType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/SemNamedType.java new file mode 100644 index 000000000000..7e7b7ed949e8 --- /dev/null +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/SemNamedType.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.wso2.ballerinalang.compiler.semantics.model.types; + +import io.ballerina.types.SemType; + +import java.util.Optional; + +/** + * Represents a sem-type and its user-specified string representation. + * + * @param semType Sem-type representation of a type + * @param optName User-specified string representation for the type, if available + */ +public record SemNamedType(SemType semType, Optional optName) { +} diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangPackage.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangPackage.java index 2b53aabcaf12..36ce13aa0db5 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangPackage.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangPackage.java @@ -20,6 +20,7 @@ import io.ballerina.projects.internal.ModuleContextDataHolder; import io.ballerina.tools.diagnostics.Diagnostic; import io.ballerina.tools.diagnostics.DiagnosticSeverity; +import io.ballerina.types.Env; import org.ballerinalang.compiler.CompilerPhase; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.PackageID; @@ -92,7 +93,9 @@ public class BLangPackage extends BLangNode implements PackageNode { public ModuleContextDataHolder moduleContextDataHolder; - public BLangPackage() { + public final Env semtypeEnv; + + public BLangPackage(Env env) { this.compUnits = new ArrayList<>(); this.imports = new ArrayList<>(); this.xmlnsList = new ArrayList<>(); @@ -110,6 +113,7 @@ public BLangPackage() { this.testablePkgs = new ArrayList<>(); this.flagSet = EnumSet.noneOf(Flag.class); this.diagnostics = new TreeSet<>(new DiagnosticComparator()); + this.semtypeEnv = env; } @Override diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java index 58196fac987d..4a73e3143fe0 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTestablePackage.java @@ -17,6 +17,8 @@ */ package org.wso2.ballerinalang.compiler.tree; +import io.ballerina.types.Env; + import java.util.HashMap; import java.util.Map; @@ -31,6 +33,11 @@ public class BLangTestablePackage extends BLangPackage { private final Map mockFunctionNamesMap = new HashMap<>(); private final Map isLegacyMockingMap = new HashMap<>(); + + public BLangTestablePackage(Env env) { + super(env); + } + public Map getMockFunctionNamesMap() { return mockFunctionNamesMap; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTypeDefinition.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTypeDefinition.java index d54361654d93..ff3129519fad 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTypeDefinition.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/BLangTypeDefinition.java @@ -18,6 +18,7 @@ package org.wso2.ballerinalang.compiler.tree; +import io.ballerina.types.SemType; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.tree.AnnotationAttachmentNode; import org.ballerinalang.model.tree.IdentifierNode; @@ -57,6 +58,10 @@ public class BLangTypeDefinition extends BLangNode implements TypeDefinition { public int cycleDepth = -1; + // SemType Integration + public SemType semType; + public int semCycleDepth = -1; + public BLangTypeDefinition() { this.annAttachments = new ArrayList<>(); this.flagSet = EnumSet.noneOf(Flag.class); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangUnaryExpr.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangUnaryExpr.java index 0b6dfa3c4bab..4d182ae04c30 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangUnaryExpr.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/expressions/BLangUnaryExpr.java @@ -41,8 +41,6 @@ public class BLangUnaryExpr extends BLangExpression implements UnaryExpressionNo // Semantic Data public BOperatorSymbol opSymbol; - public boolean isConstant; - @Override public BLangExpression getExpression() { return expr; diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/types/BLangType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/types/BLangType.java index cd9eb532ddb9..50f3bdc78199 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/types/BLangType.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/tree/types/BLangType.java @@ -17,6 +17,7 @@ */ package org.wso2.ballerinalang.compiler.tree.types; +import io.ballerina.types.Definition; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.tree.types.TypeNode; import org.wso2.ballerinalang.compiler.tree.BLangNode; @@ -36,6 +37,7 @@ public abstract class BLangType extends BLangNode implements TypeNode { public boolean nullable; public boolean grouped; public Set flagSet = EnumSet.noneOf(Flag.class); + public Definition defn; @Override public boolean isNullable() { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/ImmutableTypeCloner.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/ImmutableTypeCloner.java index ea65959446a4..fe11dda777dd 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/ImmutableTypeCloner.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/ImmutableTypeCloner.java @@ -88,8 +88,6 @@ */ public final class ImmutableTypeCloner { - private static final String AND_READONLY_SUFFIX = " & readonly"; - private ImmutableTypeCloner() { } @@ -98,8 +96,8 @@ public static BType getEffectiveImmutableType(Location pos, Types types, SymbolTable symTable, BLangAnonymousModelHelper anonymousModelHelper, Names names) { return getImmutableIntersectionType(pos, types, type, env, env.enclPkg.packageID, env.scope.owner, - symTable, anonymousModelHelper, names, new HashSet<>(), - new HashSet<>()).effectiveType; + symTable, anonymousModelHelper, names, new HashSet<>(), + new HashSet<>()).effectiveType; } public static BType getEffectiveImmutableType(Location pos, Types types, @@ -107,8 +105,8 @@ public static BType getEffectiveImmutableType(Location pos, Types types, BSymbol owner, SymbolTable symTable, BLangAnonymousModelHelper anonymousModelHelper, Names names) { return getImmutableIntersectionType(pos, types, type, null, pkgId, owner, - symTable, anonymousModelHelper, names, new HashSet<>(), - new HashSet<>()).effectiveType; + symTable, anonymousModelHelper, names, new HashSet<>(), + new HashSet<>()).effectiveType; } public static BIntersectionType getImmutableIntersectionType(Location pos, Types types, @@ -169,7 +167,7 @@ public static BType getImmutableType(Location pos, Types types, BType type, Symb return symTable.semanticError; } - if (types.isInherentlyImmutableType(type) || Symbols.isFlagOn(type.flags, Flags.READONLY)) { + if (types.isInherentlyImmutableType(type) || Symbols.isFlagOn(type.getFlags(), Flags.READONLY)) { return type; } @@ -192,7 +190,7 @@ private static BIntersectionType getImmutableIntersectionType(Location pos, Set unresolvedTypes) { BType refType = Types.getReferredType(bType); SelectivelyImmutableReferenceType type = (SelectivelyImmutableReferenceType) refType; - if (refType.tag == TypeTags.INTERSECTION && Symbols.isFlagOn(refType.flags, Flags.READONLY)) { + if (refType.tag == TypeTags.INTERSECTION && Symbols.isFlagOn(refType.getFlags(), Flags.READONLY)) { return (BIntersectionType) refType; } @@ -219,12 +217,7 @@ private static BIntersectionType setImmutableType(Location pos, Types types, case TypeTags.XML_ELEMENT: case TypeTags.XML_PI: BXMLSubType origXmlSubType = (BXMLSubType) type; - - // TODO: 4/28/20 Check tsymbol - BXMLSubType immutableXmlSubType = - new BXMLSubType(origXmlSubType.tag, - Names.fromString(origXmlSubType.name.getValue().concat(AND_READONLY_SUFFIX)), - origXmlSubType.flags | Flags.READONLY); + BXMLSubType immutableXmlSubType = BXMLSubType.newImmutableXMLSubType(origXmlSubType); BIntersectionType immutableXmlSubTypeIntersectionType = createImmutableIntersectionType(pkgId, owner, originalType, immutableXmlSubType, symTable); @@ -256,20 +249,7 @@ private static BIntersectionType setImmutableType(Location pos, Types types, unresolvedTypes, (BTableType) type, originalType); case TypeTags.ANY: BAnyType origAnyType = (BAnyType) type; - - BTypeSymbol immutableAnyTSymbol = getReadonlyTSymbol(names, origAnyType.tsymbol, env, pkgId, owner); - - BAnyType immutableAnyType; - if (immutableAnyTSymbol != null) { - immutableAnyType = new BAnyType(origAnyType.tag, immutableAnyTSymbol, immutableAnyTSymbol.name, - origAnyType.flags | Flags.READONLY, origAnyType.isNullable()); - immutableAnyTSymbol.type = immutableAnyType; - } else { - immutableAnyType = new BAnyType(origAnyType.tag, null, - getImmutableTypeName(names, TypeKind.ANY.typeName()), - origAnyType.flags | Flags.READONLY, origAnyType.isNullable()); - } - + BAnyType immutableAnyType = BAnyType.newImmutableBAnyType(); BIntersectionType immutableAnyIntersectionType = createImmutableIntersectionType(pkgId, owner, originalType, immutableAnyType, @@ -294,14 +274,14 @@ private static BIntersectionType defineImmutableTableType(Location pos, Types ty Names names, Set unresolvedTypes, BTableType type, BType originalType) { - BTypeSymbol immutableTableTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); + BTypeSymbol immutableTableTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); Optional immutableType = Types.getImmutableType(symTable, pkgId, type); if (immutableType.isPresent()) { return immutableType.get(); } else { Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, - originalType, new BTableType(TypeTags.TABLE, null, immutableTableTSymbol, - type.flags | Flags.READONLY), symTable)); + originalType, new BTableType(symTable.typeEnv(), null, immutableTableTSymbol, + type.getFlags() | Flags.READONLY), symTable)); } BIntersectionType immutableTableType = Types.getImmutableType(symTable, pkgId, type).orElseThrow(); @@ -336,13 +316,13 @@ private static BIntersectionType defineImmutableXMLType(Location pos, Types type Names names, Set unresolvedTypes, BXMLType type, BType originalType) { - BTypeSymbol immutableXmlTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); + BTypeSymbol immutableXmlTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); Optional immutableType = Types.getImmutableType(symTable, pkgId, type); if (immutableType.isPresent()) { return immutableType.get(); } else { Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, - originalType, new BXMLType(null, immutableXmlTSymbol, type.flags | Flags.READONLY), + originalType, new BXMLType(null, immutableXmlTSymbol, type.getFlags() | Flags.READONLY), symTable)); } @@ -360,14 +340,15 @@ private static BIntersectionType defineImmutableArrayType(Location pos, Types ty Names names, Set unresolvedTypes, BArrayType type, BType originalType) { - BTypeSymbol immutableArrayTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); + BTypeSymbol immutableArrayTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); Optional immutableType = Types.getImmutableType(symTable, pkgId, type); if (immutableType.isPresent()) { return immutableType.get(); } else { Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, - originalType, new BArrayType(null, immutableArrayTSymbol, type.size, type.state, - type.flags | Flags.READONLY), symTable)); + originalType, + new BArrayType(symTable.typeEnv(), null, immutableArrayTSymbol, type.getSize(), type.state, + type.getFlags() | Flags.READONLY), symTable)); } BIntersectionType immutableArrayType = Types.getImmutableType(symTable, pkgId, type).orElseThrow(); @@ -384,14 +365,14 @@ private static BIntersectionType defineImmutableMapType(Location pos, Types type Names names, Set unresolvedTypes, BMapType type, BType originalType) { - BTypeSymbol immutableMapTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); + BTypeSymbol immutableMapTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); Optional immutableType = Types.getImmutableType(symTable, pkgId, type); if (immutableType.isPresent()) { return immutableType.get(); } else { Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, - originalType, new BMapType(TypeTags.MAP, null, immutableMapTSymbol, - type.flags | Flags.READONLY), symTable)); + originalType, new BMapType(symTable.typeEnv(), TypeTags.MAP, null, immutableMapTSymbol, + type.getFlags() | Flags.READONLY), symTable)); } BIntersectionType immutableMapType = Types.getImmutableType(symTable, pkgId, type).orElseThrow(); @@ -418,7 +399,7 @@ private static BIntersectionType defineImmutableTupleType(Location pos, Types ty return immutableType.get(); } else { Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, - originalType, new BTupleType(origTupleTypeSymbol), symTable)); + originalType, new BTupleType(symTable.typeEnv(), origTupleTypeSymbol), symTable)); } List immutableMemTypes = new ArrayList<>(origTupleMembers.size()); @@ -432,7 +413,7 @@ private static BIntersectionType defineImmutableTupleType(Location pos, Types ty Name origTupleTypeSymbolName = Names.EMPTY; if (!originalTypeName.isEmpty()) { origTupleTypeSymbolName = origTupleTypeSymbol.name.value.isEmpty() ? Names.EMPTY : - getImmutableTypeName(names, getSymbolFQN(origTupleTypeSymbol)); + Types.getImmutableTypeName(getSymbolFQN(origTupleTypeSymbol)); tupleEffectiveImmutableType.name = origTupleTypeSymbolName; } @@ -463,11 +444,11 @@ private static BIntersectionType defineImmutableTupleType(Location pos, Types ty BTypeSymbol immutableTupleTSymbol = getReadonlyTSymbol(origTupleTypeSymbol, env, pkgId, owner, origTupleTypeSymbolName); effectiveTypeFromType.tsymbol = immutableTupleTSymbol; - effectiveTypeFromType.flags |= (type.flags | Flags.READONLY); + effectiveTypeFromType.addFlags(type.getFlags() | Flags.READONLY); immutableTupleTSymbol.type = effectiveTypeFromType; } else { - effectiveTypeFromType.flags |= (type.flags | Flags.READONLY); + effectiveTypeFromType.addFlags(type.getFlags() | Flags.READONLY); } BType effectiveType = immutableTupleIntersectionType.effectiveType; @@ -482,7 +463,7 @@ private static BIntersectionType defineImmutableTupleType(Location pos, Types ty BLangTypeDefinition typeDefinition = TypeDefBuilderHelper.addTypeDefinition(effectiveType, effectiveType.tsymbol, tupleTypeNode, env); typeDefinition.pos = pos; - effectiveType.flags |= Flags.EFFECTIVE_TYPE_DEF; + effectiveType.addFlags(Flags.EFFECTIVE_TYPE_DEF); return immutableTupleIntersectionType; } @@ -583,10 +564,11 @@ private static BIntersectionType defineImmutableRecordType(Location pos, BRecord BTypeSymbol recordTypeSymbol = origRecordType.tsymbol; BRecordTypeSymbol recordSymbol = Symbols.createRecordSymbol(recordTypeSymbol.flags | Flags.READONLY, - getImmutableTypeName(names, getSymbolFQN(recordTypeSymbol)), + Types.getImmutableTypeName(getSymbolFQN(recordTypeSymbol)), pkgID, null, env.scope.owner, pos, VIRTUAL); - BInvokableType bInvokableType = new BInvokableType(new ArrayList<>(), symTable.nilType, null); + BInvokableType bInvokableType = + new BInvokableType(symTable.typeEnv(), List.of(), symTable.nilType, null); BInvokableSymbol initFuncSymbol = Symbols.createFunctionSymbol( Flags.PUBLIC, Names.EMPTY, Names.EMPTY, env.enclPkg.symbol.pkgID, bInvokableType, env.scope.owner, false, symTable.builtinPos, VIRTUAL); @@ -594,7 +576,8 @@ private static BIntersectionType defineImmutableRecordType(Location pos, BRecord recordSymbol.scope = new Scope(recordSymbol); - BRecordType immutableRecordType = new BRecordType(recordSymbol, origRecordType.flags | Flags.READONLY); + BRecordType immutableRecordType = new BRecordType(symTable.typeEnv(), recordSymbol, + origRecordType.getFlags() | Flags.READONLY); BIntersectionType immutableRecordIntersectionType = createImmutableIntersectionType(env, originalType, immutableRecordType, @@ -632,7 +615,7 @@ private static BIntersectionType defineImmutableObjectType(Location pos, flags &= ~Flags.CLASS; BObjectTypeSymbol objectSymbol = Symbols.createObjectSymbol(flags, - getImmutableTypeName(names, + Types.getImmutableTypeName( getSymbolFQN(origObjectTSymbol)), pkgID, null, env.scope.owner, pos, VIRTUAL); @@ -640,7 +623,8 @@ private static BIntersectionType defineImmutableObjectType(Location pos, defineObjectFunctions(objectSymbol, origObjectTSymbol, names, symTable); - BObjectType immutableObjectType = new BObjectType(objectSymbol, origObjectType.flags | Flags.READONLY); + BObjectType immutableObjectType = + new BObjectType(symTable.typeEnv(), objectSymbol, origObjectType.getFlags() | Flags.READONLY); immutableObjectType.typeIdSet = origObjectType.typeIdSet; BIntersectionType immutableObjectIntersectionType = createImmutableIntersectionType(env, originalType, @@ -684,7 +668,8 @@ public static void defineObjectFunctions(BObjectTypeSymbol immutableObjectSymbol Name funcName = Names.fromString(Symbols.getAttachedFuncSymbolName(immutableObjectSymbol.name.value, origFunc.funcName.value)); BInvokableSymbol immutableFuncSymbol = - ASTBuilderUtil.duplicateFunctionDeclarationSymbol(origFunc.symbol, immutableObjectSymbol, + ASTBuilderUtil.duplicateFunctionDeclarationSymbol(symTable.typeEnv(), origFunc.symbol, + immutableObjectSymbol, funcName, immutableObjectSymbol.pkgID, symTable.builtinPos, VIRTUAL); immutableFuncs.add(new BAttachedFunction(origFunc.funcName, immutableFuncSymbol, @@ -707,7 +692,7 @@ private static BIntersectionType defineImmutableUnionType(Location pos, Types ty if (immutableTypeOptional.isPresent()) { return immutableTypeOptional.get(); } else { - BUnionType immutableUnionType = BUnionType.create(origUnionTypeSymbol); + BUnionType immutableUnionType = BUnionType.create(symTable.typeEnv(), origUnionTypeSymbol); Types.addImmutableType(symTable, pkgId, type, createImmutableIntersectionType(pkgId, owner, originalType, immutableUnionType, symTable)); } @@ -762,27 +747,23 @@ private static BIntersectionType defineImmutableBuiltInUnionType(Location pos, T private static BAnydataType defineImmutableAnydataType(SymbolEnv env, PackageID pkgId, BSymbol owner, Names names, BAnydataType type) { - BTypeSymbol immutableAnydataTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); + BTypeSymbol immutableAnydataTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); if (immutableAnydataTSymbol != null) { - BAnydataType immutableAnydataType = - new BAnydataType(immutableAnydataTSymbol, - immutableAnydataTSymbol.name, type.flags | Flags.READONLY, + BAnydataType immutableAnydataType = BAnydataType.newImmutableBAnydataType(type, immutableAnydataTSymbol, + immutableAnydataTSymbol.name, type.isNullable()); immutableAnydataTSymbol.type = immutableAnydataType; return immutableAnydataType; } - return new BAnydataType(null, - getImmutableTypeName(names, TypeKind.ANYDATA.typeName()), - type.flags | Flags.READONLY, type.isNullable()); + return BAnydataType.newImmutableBAnydataType(type, null, + Types.getImmutableTypeName(TypeKind.ANYDATA.typeName()), type.isNullable()); } private static BJSONType defineImmutableJsonType(SymbolEnv env, PackageID pkgId, BSymbol owner, Names names, BJSONType type) { - BTypeSymbol immutableJsonTSymbol = getReadonlyTSymbol(names, type.tsymbol, env, pkgId, owner); - BJSONType immutableJsonType = new BJSONType(immutableJsonTSymbol, - type.isNullable(), - type.flags | Flags.READONLY); + BTypeSymbol immutableJsonTSymbol = getReadonlyTSymbol(type.tsymbol, env, pkgId, owner); + BJSONType immutableJsonType = BJSONType.newImmutableBJSONType(type, immutableJsonTSymbol, type.isNullable()); if (immutableJsonTSymbol != null) { immutableJsonTSymbol.type = immutableJsonType; } @@ -804,7 +785,7 @@ private static BIntersectionType handleImmutableUnionType(Location pos, Types ty String originalTypeName = origUnionTypeSymbol == null ? "" : origUnionTypeSymbol.name.getValue(); if (!originalTypeName.isEmpty()) { - unionEffectiveImmutableType.name = getImmutableTypeName(names, getSymbolFQN(origUnionTypeSymbol)); + unionEffectiveImmutableType.name = Types.getImmutableTypeName(getSymbolFQN(origUnionTypeSymbol)); } for (BType memberType : originalMemberList) { @@ -829,25 +810,25 @@ private static BIntersectionType handleImmutableUnionType(Location pos, Types ty BTypeSymbol immutableUnionTSymbol = getReadonlyTSymbol(origUnionTypeSymbol, env, pkgId, owner, origUnionTypeSymbol.name.value.isEmpty() ? Names.EMPTY : - getImmutableTypeName(names, getSymbolFQN(origUnionTypeSymbol))); + Types.getImmutableTypeName(getSymbolFQN(origUnionTypeSymbol))); immutableType.effectiveType.tsymbol = immutableUnionTSymbol; - immutableType.effectiveType.flags |= (type.flags | Flags.READONLY); + immutableType.effectiveType.addFlags(type.getFlags() | Flags.READONLY); immutableUnionTSymbol.type = immutableType.effectiveType; } else { - immutableType.effectiveType.flags |= (type.flags | Flags.READONLY); + immutableType.effectiveType.addFlags(type.getFlags() | Flags.READONLY); } return immutableType; } - private static BTypeSymbol getReadonlyTSymbol(Names names, BTypeSymbol originalTSymbol, SymbolEnv env, + private static BTypeSymbol getReadonlyTSymbol(BTypeSymbol originalTSymbol, SymbolEnv env, PackageID pkgId, BSymbol owner) { if (originalTSymbol == null) { return null; } - return getReadonlyTSymbol(originalTSymbol, env, pkgId, owner, getImmutableTypeName(names, originalTSymbol)); + return getReadonlyTSymbol(originalTSymbol, env, pkgId, owner, getImmutableTypeName(originalTSymbol)); } private static BTypeSymbol getReadonlyTSymbol(BTypeSymbol originalTSymbol, SymbolEnv env, PackageID pkgId, @@ -877,16 +858,8 @@ private static String getSymbolFQN(BTypeSymbol originalTSymbol) { getMajorVersion(pkgID.version.value) + ":" + originalTSymbol.name; } - private static Name getImmutableTypeName(Names names, BTypeSymbol originalTSymbol) { - return getImmutableTypeName(names, originalTSymbol.name.getValue()); - } - - private static Name getImmutableTypeName(Names names, String origName) { - if (origName.isEmpty()) { - return Names.EMPTY; - } - - return Names.fromString("(".concat(origName).concat(AND_READONLY_SUFFIX).concat(")")); + private static Name getImmutableTypeName(BTypeSymbol originalTSymbol) { + return Types.getImmutableTypeName(originalTSymbol.name.getValue()); } private static BIntersectionType createImmutableIntersectionType(SymbolEnv env, BType nonReadOnlyType, @@ -910,7 +883,7 @@ private static BIntersectionType createImmutableIntersectionType(PackageID pkgId }}; BIntersectionType intersectionType = new BIntersectionType(intersectionTypeSymbol, constituentTypes, - effectiveType, Flags.READONLY | effectiveType.flags); + effectiveType, Flags.READONLY | effectiveType.getFlags()); intersectionTypeSymbol.type = intersectionType; return intersectionType; } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Names.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Names.java index a57fdf0b7d68..fc540bdfa491 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Names.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Names.java @@ -127,7 +127,9 @@ public class Names { public static final Name XML_COMMENT = new Name(STRING_XML_COMMENT); public static final Name XML_TEXT = new Name(STRING_XML_TEXT); public static final Name REGEXP_TYPE = new Name(STRING_REGEXP); - + public static final Name TRUE = new Name("true"); + public static final Name FALSE = new Name("false"); + // Names related to transactions. public static final Name TRANSACTION_PACKAGE = new Name("transactions"); public static final Name TRANSACTION_INFO_RECORD = new Name("Info"); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/TypeDefBuilderHelper.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/TypeDefBuilderHelper.java index 727bbc9e1cdb..380455d75304 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/TypeDefBuilderHelper.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/TypeDefBuilderHelper.java @@ -17,6 +17,7 @@ package org.wso2.ballerinalang.compiler.util; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.Flag; import org.ballerinalang.model.elements.MarkdownDocAttachment; @@ -142,14 +143,12 @@ public static BLangFunction createInitFunctionForStructureType(BSymbol symbol, Name suffix, SymbolTable symTable, BType type) { - return createInitFunctionForStructureType(symbol, env, names, suffix, type, symTable.nilType); + return createInitFunctionForStructureType(symTable.typeEnv(), symbol, env, names, suffix, type, + symTable.nilType); } - public static BLangFunction createInitFunctionForStructureType(BSymbol symbol, - SymbolEnv env, - Names names, - Name suffix, - BType type, + public static BLangFunction createInitFunctionForStructureType(Env typeEnv, BSymbol symbol, SymbolEnv env, + Names names, Name suffix, BType type, BType returnType) { String structTypeName = type.tsymbol.name.value; BLangFunction initFunction = ASTBuilderUtil.createInitFunctionWithNilReturn(null, structTypeName, suffix); @@ -165,7 +164,7 @@ public static BLangFunction createInitFunctionForStructureType(BSymbol symbol, initFunction.flagSet.add(Flag.ATTACHED); // Create the function type - initFunction.setBType(new BInvokableType(new ArrayList<>(), returnType, null)); + initFunction.setBType(new BInvokableType(typeEnv, List.of(), returnType, null)); // Create the function symbol Name funcSymbolName = Names.fromString(Symbols.getAttachedFuncSymbolName(structTypeName, suffix.value)); diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Unifier.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Unifier.java index 51225b103c49..1bb911853161 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Unifier.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/util/Unifier.java @@ -17,6 +17,7 @@ package org.wso2.ballerinalang.compiler.util; +import io.ballerina.types.Env; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.tree.NodeKind; import org.ballerinalang.model.types.TypeKind; @@ -31,7 +32,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BAnyType; import org.wso2.ballerinalang.compiler.semantics.model.types.BAnydataType; import org.wso2.ballerinalang.compiler.semantics.model.types.BArrayType; -import org.wso2.ballerinalang.compiler.semantics.model.types.BBuiltInRefType; import org.wso2.ballerinalang.compiler.semantics.model.types.BErrorType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFiniteType; import org.wso2.ballerinalang.compiler.semantics.model.types.BFutureType; @@ -89,14 +89,16 @@ public class Unifier implements BTypeVisitor { private SymbolEnv env; private Types types; private BLangDiagnosticLog dlog; + private Env typeEnv; - public BType build(BType originalType, BType expType, BLangInvocation invocation, Types types, + public BType build(Env typeEnv, BType originalType, BType expType, BLangInvocation invocation, Types types, SymbolTable symbolTable, BLangDiagnosticLog dlog) { this.isInvocation = invocation != null; if (this.isInvocation) { this.invocation = invocation; createParamMap(invocation); } + this.typeEnv = typeEnv; this.types = types; this.symbolTable = symbolTable; this.dlog = dlog; @@ -105,12 +107,13 @@ public BType build(BType originalType, BType expType, BLangInvocation invocation return newType; } - public BType build(BType originalType) { - return build(originalType, null, null, null, null, null); + public BType build(Env typeEnv, BType originalType) { + return build(typeEnv, originalType, null, null, null, null, null); } - public void validate(BType returnType, BLangFunction function, SymbolTable symbolTable, SymbolEnv env, Types types, - BLangDiagnosticLog dlog) { + public void validate(Env typeEnv, BType returnType, BLangFunction function, SymbolTable symbolTable, SymbolEnv env, + Types types, BLangDiagnosticLog dlog) { + this.typeEnv = typeEnv; this.function = function; this.symbolTable = symbolTable; this.env = env; @@ -125,11 +128,6 @@ public BType visit(BType originalType, BType expType) { return originalType; } - @Override - public BType visit(BBuiltInRefType originalType, BType expType) { - return originalType; - } - @Override public BType visit(BAnyType originalType, BType expType) { return originalType; @@ -154,8 +152,8 @@ public BType visit(BMapType originalType, BType expType) { return symbolTable.semanticError; } - BMapType newMType = new BMapType(originalType.tag, newConstraint, null); - setFlags(newMType, originalType.flags); + BMapType newMType = new BMapType(typeEnv, originalType.tag, newConstraint, null); + setFlags(newMType, originalType.getFlags()); return newMType; } @@ -174,7 +172,7 @@ public BType visit(BXMLType originalType, BType expType) { } BXMLType newXMLType = new BXMLType(newConstraint, null); - setFlags(newXMLType, originalType.flags); + setFlags(newXMLType, originalType.getFlags()); return newXMLType; } @@ -197,8 +195,9 @@ public BType visit(BArrayType originalType, BType expType) { return symbolTable.semanticError; } - BArrayType newArrayType = new BArrayType(newElemType, null, originalType.size, originalType.state); - setFlags(newArrayType, originalType.flags); + BArrayType newArrayType = new BArrayType(typeEnv, newElemType, null, originalType.getSize(), + originalType.state); + setFlags(newArrayType, originalType.getFlags()); return newArrayType; } @@ -253,7 +252,7 @@ public BType visit(BTupleType originalType, BType expType) { BType member = tupleTypes.get(i); BType expMember = expTupleTypes.get(j); BType newMem = member.accept(this, expMember); - BVarSymbol varSymbol = new BVarSymbol(newMem.flags, null, null, newMem, null, null, null); + BVarSymbol varSymbol = new BVarSymbol(newMem.getFlags(), null, null, newMem, null, null, null); members.add(new BTupleMember(newMem, varSymbol)); if (isSemanticErrorInInvocation(newMem)) { @@ -284,9 +283,9 @@ public BType visit(BTupleType originalType, BType expType) { return expType != null ? expType : originalType; } - BTupleType type = new BTupleType(null, members); + BTupleType type = new BTupleType(typeEnv, members); type.restType = newRestType; - setFlags(type, originalType.flags); + setFlags(type, originalType.getFlags()); return type; } @@ -313,8 +312,8 @@ public BType visit(BStreamType originalType, BType expType) { return symbolTable.semanticError; } - BStreamType type = new BStreamType(originalType.tag, newConstraint, newError, null); - setFlags(type, originalType.flags); + BStreamType type = new BStreamType(typeEnv, originalType.tag, newConstraint, newError, null); + setFlags(type, originalType.getFlags()); return type; } @@ -340,19 +339,19 @@ public BType visit(BTableType originalType, BType expType) { return symbolTable.semanticError; } - BTableType newTableType = new BTableType(TypeTags.TABLE, newConstraint, null); + BTableType newTableType = new BTableType(typeEnv, newConstraint, null); newTableType.keyTypeConstraint = null; newTableType.fieldNameList = originalType.fieldNameList; newTableType.constraintPos = originalType.constraintPos; newTableType.isTypeInlineDefined = originalType.isTypeInlineDefined; newTableType.keyPos = originalType.keyPos; - setFlags(newTableType, originalType.flags); + setFlags(newTableType, originalType.getFlags()); return newTableType; } @Override public BType visit(BInvokableType originalType, BType expType) { - if (Symbols.isFlagOn(originalType.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(originalType.getFlags(), Flags.ANY_FUNCTION)) { return originalType; } @@ -412,8 +411,8 @@ public BType visit(BInvokableType originalType, BType expType) { } } - BType type = new BInvokableType(paramTypes, newRestType, retType, null); - setFlags(type, originalType.flags); + BType type = new BInvokableType(typeEnv, paramTypes, newRestType, retType, null); + setFlags(type, originalType.getFlags()); return type; } @@ -450,7 +449,7 @@ public BType visit(BUnionType originalType, BType expType) { return symbolTable.semanticError; } - if (newMember == member && Symbols.isFlagOn(member.flags, Flags.PARAMETERIZED)) { + if (newMember == member && Symbols.isFlagOn(member.getFlags(), Flags.PARAMETERIZED)) { return expType; } } @@ -466,8 +465,8 @@ public BType visit(BUnionType originalType, BType expType) { return originalType; } - BUnionType type = BUnionType.create(null, newMemberTypes); - setFlags(type, originalType.flags); + BUnionType type = BUnionType.create(originalType.env, null, newMemberTypes); + setFlags(type, originalType.getFlags()); return type; } @@ -488,7 +487,7 @@ public BType visit(BIntersectionType originalType, BType expType) { BIntersectionType type = new BIntersectionType(null, (LinkedHashSet) originalType.getConstituentTypes(), newEffectiveType); - setFlags(type, originalType.flags); + setFlags(type, originalType.getFlags()); return originalType; } @@ -511,9 +510,8 @@ public BType visit(BFutureType originalType, BType expType) { return symbolTable.semanticError; } - BFutureType newFutureType = new BFutureType(originalType.tag, newConstraint, null, - originalType.workerDerivative); - setFlags(newFutureType, originalType.flags); + BFutureType newFutureType = new BFutureType(typeEnv, newConstraint, null, originalType.workerDerivative); + setFlags(newFutureType, originalType.getFlags()); return newFutureType; } @@ -537,8 +535,8 @@ public BType visit(BTypedescType originalType, BType expType) { return symbolTable.semanticError; } - BTypedescType newTypedescType = new BTypedescType(newConstraint, null); - setFlags(newTypedescType, originalType.flags); + BTypedescType newTypedescType = new BTypedescType(typeEnv, newConstraint, null); + setFlags(newTypedescType, originalType.getFlags()); return newTypedescType; } @@ -569,7 +567,7 @@ public BType visit(BParameterizedType originalType, BType expType) { // Log an error only if the user has not explicitly passed an argument. If the passed // argument is invalid, the type checker will log the error. dlog.error(invocation.pos, DiagnosticErrorCode.INCOMPATIBLE_TYPE_FOR_INFERRED_TYPEDESC_VALUE, - paramVarName, paramSymbolTypedescType, new BTypedescType(expType, null)); + paramVarName, paramSymbolTypedescType, new BTypedescType(typeEnv, expType, null)); return symbolTable.semanticError; } BType type = paramValueTypes.get(paramVarName); @@ -698,7 +696,7 @@ private BLangNamedArgsExpression createTypedescExprNamedArg(BType expType, Strin BLangTypedescExpr typedescExpr = (BLangTypedescExpr) TreeBuilder.createTypeAccessNode(); typedescExpr.pos = this.symbolTable.builtinPos; typedescExpr.resolvedType = expType; - typedescExpr.setBType(new BTypedescType(expType, null)); + typedescExpr.setBType(new BTypedescType(typeEnv, expType, null)); BLangNamedArgsExpression namedArgsExpression = (BLangNamedArgsExpression) TreeBuilder.createNamedArgNode(); BLangIdentifier identifierNode = (BLangIdentifier) TreeBuilder.createIdentifierNode(); @@ -829,7 +827,7 @@ private void populateParamMapFromTupleRestArg(List params, int curre } private void setFlags(BType type, long originalFlags) { - type.flags = originalFlags & (~Flags.PARAMETERIZED); + type.setFlags(originalFlags & (~Flags.PARAMETERIZED)); } private int getParamPosition(BVarSymbol sym) { @@ -960,7 +958,7 @@ private BType getMatchingTypeForInferrableType(BType originalType, BType expType BType referredOriginalType = Types.getImpliedType(originalType); if (referredOriginalType.tag == TypeTags.UNION) { for (BType memberType : ((BUnionType) referredOriginalType).getMemberTypes()) { - if (!Symbols.isFlagOn(memberType.flags, Flags.PARAMETERIZED)) { + if (!Symbols.isFlagOn(memberType.getFlags(), Flags.PARAMETERIZED)) { continue; } @@ -1051,7 +1049,7 @@ private boolean refersInferableParamName(List paramsWithInferredTypedesc } return refersInferableParamName(paramsWithInferredTypedescDefault, completionType, unresolvedTypes); case TypeTags.INVOKABLE: - if (Symbols.isFlagOn(type.flags, Flags.ANY_FUNCTION)) { + if (Symbols.isFlagOn(type.getFlags(), Flags.ANY_FUNCTION)) { return false; } BInvokableType invokableType = (BInvokableType) type; @@ -1136,7 +1134,7 @@ private List getParamsWithInferredTypedescDefault(List param // If the `expType` is `int|string|boolean` and the original type is `t|string` then the expected type for `t` // is `int|boolean`. private BType getExpectedTypeForInferredTypedescMember(BUnionType originalType, BType expType, BType member) { - if (expType == null || !this.isInvocation || !Symbols.isFlagOn(member.flags, Flags.PARAMETERIZED)) { + if (expType == null || !this.isInvocation || !Symbols.isFlagOn(member.getFlags(), Flags.PARAMETERIZED)) { return null; } @@ -1185,7 +1183,7 @@ private BType getExpectedTypeForInferredTypedescMember(BUnionType originalType, return expectedTypesSet.iterator().next(); } - return BUnionType.create(null, expectedTypesSet); + return BUnionType.create(typeEnv, null, expectedTypesSet); } private boolean isSameTypeOrError(BType newType, BType originalType) { diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java index e9ae324c0d33..da348731b9ec 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/programfile/ProgramFileConstants.java @@ -25,9 +25,9 @@ public final class ProgramFileConstants { public static final int MAGIC_NUMBER = 0xBA1DA4CE; public static final short VERSION_NUMBER = 50; - public static final int BIR_VERSION_NUMBER = 73; - public static final short MIN_SUPPORTED_VERSION = 73; - public static final short MAX_SUPPORTED_VERSION = 73; + public static final int BIR_VERSION_NUMBER = 74; + public static final short MIN_SUPPORTED_VERSION = 74; + public static final short MAX_SUPPORTED_VERSION = 74; // todo move this to a proper place public static final String[] SUPPORTED_PLATFORMS = {"java21", "java17", "java11"}; diff --git a/compiler/ballerina-lang/src/test/java/org/wso2/ballerinalang/compiler/diagnostic/BLangDiagnosticLogTest.java b/compiler/ballerina-lang/src/test/java/org/wso2/ballerinalang/compiler/diagnostic/BLangDiagnosticLogTest.java index 111755979b37..e2159787bbb9 100644 --- a/compiler/ballerina-lang/src/test/java/org/wso2/ballerinalang/compiler/diagnostic/BLangDiagnosticLogTest.java +++ b/compiler/ballerina-lang/src/test/java/org/wso2/ballerinalang/compiler/diagnostic/BLangDiagnosticLogTest.java @@ -28,6 +28,7 @@ import io.ballerina.tools.diagnostics.DiagnosticInfo; import io.ballerina.tools.diagnostics.DiagnosticSeverity; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.Env; import org.ballerinalang.compiler.CompilerOptionName; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.PackageID; @@ -62,7 +63,7 @@ public void setup() { @Test public void testLogDiagnosticWithModuleDescriptor() { - BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(); + BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(new Env()); PackageID packageID = createPackageID("org.diagnostic.log", ".", "1.0.0"); PackageCache packageCache = PackageCache.getInstance(context); @@ -80,7 +81,7 @@ public void testLogDiagnosticWithModuleDescriptor() { @Test public void testLogDiagnosticWithPackageID() { - BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(); + BLangPackage pkgNode = (BLangPackage) TreeBuilder.createPackageNode(new Env()); PackageID packageID = createPackageID("org.diagnostic.log", ".", "1.0.0"); PackageCache packageCache = PackageCache.getInstance(context); diff --git a/docs/bir-spec/src/main/resources/kaitai/bir.ksy b/docs/bir-spec/src/main/resources/kaitai/bir.ksy index c4ac5bb12572..4114e134e913 100644 --- a/docs/bir-spec/src/main/resources/kaitai/bir.ksy +++ b/docs/bir-spec/src/main/resources/kaitai/bir.ksy @@ -96,8 +96,6 @@ types: type: s4 - id: type_flag type: s8 - - id: type_special_flag - type: s4 - id: type_structure type: switch-on: type_tag @@ -122,6 +120,238 @@ types: instances: name_as_str: value: _root.constant_pool.constant_pool_entries[name_index].cp_info.as.value + sem_named_type: + seq: + - id: semtype + type: semtype_info + - id: optional_name + type: nullable_str_info + nullable_str_info: + seq: + - id: has_non_null_string + type: u1 + - id: str_cp_index + type: s4 + if: has_non_null_string == 1 + semtype_info: + seq: + - id: has_semtype + type: u1 + - id: semtype + type: semtype_internal + if: has_semtype == 1 + semtype_internal: + seq: + - id: is_uniform_type_bit_set + type: u1 + - id: uniform_type_bit_set + type: s4 + if: is_uniform_type_bit_set == 1 + - id: complex_semtype + type: semtype_complex + if: is_uniform_type_bit_set == 0 + semtype_complex: + seq: + - id: all_bit_set + type: s4 + - id: some_bit_set + type: s4 + - id: subtype_data_list_length + type: s1 + - id: proper_subtype_data + type: semtype_proper_subtype_data + repeat: expr + repeat-expr: subtype_data_list_length + semtype_proper_subtype_data: + seq: + - id: proper_subtype_data_kind + type: s1 + - id: bdd + type: semtype_bdd + if: proper_subtype_data_kind == 1 + - id: int_subtype + type: semtype_int_subtype + if: proper_subtype_data_kind == 2 + - id: boolean_subtype + type: semtype_boolean_subtype + if: proper_subtype_data_kind == 3 + - id: float_subtype + type: semtype_float_subtype + if: proper_subtype_data_kind == 4 + - id: decimal_subtype + type: semtype_decimal_subtype + if: proper_subtype_data_kind == 5 + - id: string_subtype + type: semtype_string_subtype + if: proper_subtype_data_kind == 6 + - id: xml_subtype + type: semtype_xml_subtype + if: proper_subtype_data_kind == 7 + semtype_bdd: + seq: + - id: is_bdd_node + type: u1 + - id: bdd_node + type: semtype_bdd_node + if: is_bdd_node == 1 + - id: bdd_all_or_nothing + type: u1 + if: is_bdd_node == 0 + semtype_bdd_node: + seq: + - id: is_rec_atom + type: u1 + - id: rec_atom_index + type: s4 + if: is_rec_atom == 1 + - id: target_kind + type: s4 + if: is_rec_atom == 1 and rec_atom_index > 1 + - id: type_atom + type: semtype_type_atom + if: is_rec_atom == 0 + - id: bdd_node_left + type: semtype_bdd + - id: bdd_node_middle + type: semtype_bdd + - id: bdd_node_right + type: semtype_bdd + semtype_type_atom: + seq: + - id: type_atom_index + type: s4 + - id: type_atom_kind + type: s1 + - id: mapping_atomic_type + type: semtype_mapping_atomic_type + if: type_atom_kind == 1 + - id: list_atomic_type + type: semtype_list_atomic_type + if: type_atom_kind == 2 + - id: function_atomic_type + type: semtype_function_atomic_type + if: type_atom_kind == 3 + - id: cell_atomic_type + type: semtype_cell_atomic_type + if: type_atom_kind == 4 + semtype_mapping_atomic_type: + seq: + - id: names_length + type: s4 + - id: names + type: s4 + repeat: expr + repeat-expr: names_length + - id: types_length + type: s4 + - id: types + type: semtype_info + repeat: expr + repeat-expr: types_length + - id: rest + type: semtype_info + semtype_list_atomic_type: + seq: + - id: initial_list_size + type: s4 + - id: initial + type: semtype_info + repeat: expr + repeat-expr: initial_list_size + - id: fixed_length + type: s4 + - id: rest + type: semtype_info + semtype_function_atomic_type: + seq: + - id: param_type + type: semtype_info + - id: ret_type + type: semtype_info + - id: qualifier_type + type: semtype_info + - id: is_generic + type: u1 + semtype_cell_atomic_type: + seq: + - id: ty + type: semtype_info + - id: mut + type: s1 + semtype_int_subtype: + seq: + - id: ranges_length + type: s4 + - id: x + type: semtype_range + repeat: expr + repeat-expr: ranges_length + semtype_range: + seq: + - id: min + type: s8 + - id: max + type: s8 + semtype_boolean_subtype: + seq: + - id: value + type: u1 + semtype_float_subtype: + seq: + - id: allowed + type: u1 + - id: values_length + type: s4 + - id: values + type: f8 + repeat: expr + repeat-expr: values_length + semtype_decimal_subtype: + seq: + - id: allowed + type: u1 + - id: values_length + type: s4 + - id: values + type: semtype_enumerable_decimal + repeat: expr + repeat-expr: values_length + semtype_enumerable_decimal: + seq: + - id: scale + type: s4 + - id: unscaled_value_bytes_length + type: s4 + - id: unscaled_value_bytes + size: unscaled_value_bytes_length + semtype_string_subtype: + seq: + - id: allowed + type: u1 + - id: values_length + type: s4 + - id: values + type: semtype_enumerable_string + repeat: expr + repeat-expr: values_length + - id: allowed1 + type: u1 + - id: values_length1 + type: s4 + - id: values1 + type: semtype_enumerable_string + repeat: expr + repeat-expr: values_length1 + semtype_enumerable_string: + seq: + - id: string_cp_index + type: s4 + semtype_xml_subtype: + seq: + - id: primitives + type: s4 + - id: sequence + type: semtype_bdd type_array: seq: - id: state @@ -170,18 +400,10 @@ types: type: s8 - id: value_space_size type: s4 - - id: finite_values - type: finite_value + - id: value_space + type: sem_named_type repeat: expr repeat-expr: value_space_size - finite_value: - seq: - - id : type_cp_index - type: s4 - - id: value_length - type: s4 - - id: value - size: value_length closure_symbol_body: seq: - id: name_cp_index diff --git a/docs/bir-spec/src/test/java/org/ballerinalang/birspec/BIRTestUtils.java b/docs/bir-spec/src/test/java/org/ballerinalang/birspec/BIRTestUtils.java index 636262ee19af..23e97cefd4af 100644 --- a/docs/bir-spec/src/test/java/org/ballerinalang/birspec/BIRTestUtils.java +++ b/docs/bir-spec/src/test/java/org/ballerinalang/birspec/BIRTestUtils.java @@ -649,7 +649,7 @@ private static void assertType(Bir.ConstantPoolEntry constantPoolEntry, BType ex Bir.TypeInfo typeInfo = ((Bir.ShapeCpInfo) constantPoolEntry.cpInfo()).shape(); Assert.assertEquals(typeInfo.typeTag().id(), expectedValue.tag); Assert.assertEquals(typeInfo.nameAsStr(), expectedValue.name.getValue()); - assertFlags(typeInfo.typeFlag(), expectedValue.flags); + assertFlags(typeInfo.typeFlag(), expectedValue.getFlags()); KaitaiStruct typeStructure = typeInfo.typeStructure(); if (typeStructure instanceof Bir.TypeObjectOrService objectOrService) { diff --git a/langlib/lang.__internal/src/main/java/org/ballerinalang/langlib/internal/SetNarrowType.java b/langlib/lang.__internal/src/main/java/org/ballerinalang/langlib/internal/SetNarrowType.java index 3518836a2fd0..89c3b5342375 100644 --- a/langlib/lang.__internal/src/main/java/org/ballerinalang/langlib/internal/SetNarrowType.java +++ b/langlib/lang.__internal/src/main/java/org/ballerinalang/langlib/internal/SetNarrowType.java @@ -29,6 +29,7 @@ import io.ballerina.runtime.api.values.BTypedesc; import java.util.HashMap; +import java.util.concurrent.atomic.AtomicLong; /** * Native implementation of lang.internal:setNarrowType(typedesc, (any|error)[]). @@ -37,13 +38,19 @@ */ public final class SetNarrowType { + private static final AtomicLong nextNarrowTypeId = new AtomicLong(0); + private SetNarrowType() { } + private static String getTypeName() { + return "narrowType" + nextNarrowTypeId.getAndIncrement(); + } + public static BMap setNarrowType(BTypedesc td, BMap value) { RecordType recordType = (RecordType) TypeUtils.getImpliedType(value.getType()); RecordType newRecordType = - TypeCreator.createRecordType("narrowType", recordType.getPackage(), recordType.getTypeFlags(), + TypeCreator.createRecordType(getTypeName(), recordType.getPackage(), recordType.getTypeFlags(), recordType.isSealed(), recordType.getTypeFlags()); newRecordType.setFields(new HashMap<>() {{ put("value", TypeCreator.createField(td.getDescribingType(), "value", diff --git a/langlib/lang.error/src/main/java/org/ballerinalang/langlib/error/StackTrace.java b/langlib/lang.error/src/main/java/org/ballerinalang/langlib/error/StackTrace.java index 33212bd6edd9..d0b49ffca282 100644 --- a/langlib/lang.error/src/main/java/org/ballerinalang/langlib/error/StackTrace.java +++ b/langlib/lang.error/src/main/java/org/ballerinalang/langlib/error/StackTrace.java @@ -25,6 +25,7 @@ import io.ballerina.runtime.api.types.MethodType; import io.ballerina.runtime.api.types.ObjectType; import io.ballerina.runtime.api.types.PredefinedTypes; +import io.ballerina.runtime.api.types.RecordType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.utils.TypeUtils; @@ -47,6 +48,8 @@ import static io.ballerina.runtime.api.constants.RuntimeConstants.DOT; import static io.ballerina.runtime.api.constants.RuntimeConstants.EMPTY; import static io.ballerina.runtime.api.constants.RuntimeConstants.FILE_NAME_PERIOD_SEPARATOR; +import static io.ballerina.runtime.api.flags.SymbolFlags.OPTIONAL; +import static io.ballerina.runtime.api.flags.SymbolFlags.PUBLIC; import static io.ballerina.runtime.api.values.BError.CALL_STACK_ELEMENT; /** @@ -59,21 +62,34 @@ public final class StackTrace { private StackTrace() { } + private static final ObjectType CALLSTACK_TYPE = createCallStackType(); + public static BObject stackTrace(BError value) { + CallStack callStack = new CallStack(CALLSTACK_TYPE); + callStack.callStack = getCallStackArray(value.getStackTrace()); + callStack.callStack.freezeDirect(); + return callStack; + } + + private static ObjectType createCallStackType() { + Module module = new Module("ballerina", "lang.error", null); + RecordType callStackElementType = + TypeCreator.createRecordType("CallStackElement", module, 0, Map.of( + "callableName", TypeCreator.createField(PredefinedTypes.TYPE_STRING, "callableName", 0), + "moduleName", TypeCreator.createField(PredefinedTypes.TYPE_STRING, "moduleName", OPTIONAL), + "fileName", TypeCreator.createField(PredefinedTypes.TYPE_STRING, "fileName", 0), + "lineNumber", TypeCreator.createField(PredefinedTypes.TYPE_INT, "lineNumber", 0) + ), PredefinedTypes.TYPE_NEVER, false, 0); + ObjectType callStackObjType = TypeCreator - .createObjectType("CallStack", new Module("ballerina", "lang.error", null), 0); + .createObjectType("CallStack", module, 0); callStackObjType.setMethods(new MethodType[]{}); callStackObjType .setFields(Collections.singletonMap("callStack", - TypeCreator.createField(TypeCreator.createArrayType( - PredefinedTypes.TYPE_ANY), - null, 0))); - - CallStack callStack = new CallStack(callStackObjType); - callStack.callStack = getCallStackArray(value.getStackTrace()); - callStack.callStack.freezeDirect(); - return callStack; + TypeCreator.createField(TypeCreator.createArrayType(callStackElementType), "callStack", + PUBLIC))); + return callStackObjType; } private static BArray getCallStackArray(StackTraceElement[] stackTrace) { diff --git a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibMapTest.java b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibMapTest.java index 8258de8eca9c..80db405f6fff 100644 --- a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibMapTest.java +++ b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibMapTest.java @@ -20,12 +20,12 @@ import io.ballerina.runtime.api.creators.ValueCreator; import io.ballerina.runtime.api.types.ArrayType; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; -import io.ballerina.runtime.internal.types.BMapType; import org.ballerinalang.test.BCompileUtil; import org.ballerinalang.test.BRunUtil; import org.ballerinalang.test.CompileResult; @@ -86,7 +86,7 @@ public void testEntries() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.TUPLE_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.TUPLE_TAG); assertEquals(map.size(), 3); assertEquals(map.get(StringUtils.fromString("lk")).toString(), "[\"lk\",\"Sri Lanka\"]"); assertEquals(map.get(StringUtils.fromString("us")).toString(), "[\"us\",\"USA\"]"); @@ -148,7 +148,7 @@ public void testMap() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.FLOAT_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.FLOAT_TAG); assertEquals(map.size(), 3); assertEquals(map.get(StringUtils.fromString("1")), 5.5d); assertEquals(map.get(StringUtils.fromString("2")), 11.0d); @@ -167,7 +167,7 @@ public void testFilter() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.DECIMAL_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.DECIMAL_TAG); assertEquals(map.size(), 2); assertEquals(map.get(StringUtils.fromString("1")), ValueCreator.createDecimalValue("12.34")); assertEquals(map.get(StringUtils.fromString("4")), ValueCreator.createDecimalValue("21.2")); diff --git a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibRecordTest.java b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibRecordTest.java index 72933bf4069d..020049525a2c 100644 --- a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibRecordTest.java +++ b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibRecordTest.java @@ -18,6 +18,7 @@ package org.ballerinalang.langlib.test; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.Type; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.utils.StringUtils; @@ -25,7 +26,6 @@ import io.ballerina.runtime.api.values.BMap; import io.ballerina.runtime.api.values.BString; import io.ballerina.runtime.internal.types.BArrayType; -import io.ballerina.runtime.internal.types.BMapType; import org.ballerinalang.test.BCompileUtil; import org.ballerinalang.test.BRunUtil; import org.ballerinalang.test.CompileResult; @@ -88,7 +88,7 @@ public void testEntries() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.TUPLE_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.TUPLE_TAG); assertEquals(map.size(), 2); assertEquals(map.get(StringUtils.fromString("name")).toString(), "[\"name\",\"John Doe\"]"); assertEquals(map.get(StringUtils.fromString("age")).toString(), "[\"age\",25]"); @@ -143,7 +143,7 @@ public void testMap() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.INT_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.INT_TAG); assertEquals(map.size(), 2); assertEquals(map.get(StringUtils.fromString("name")), 8L); assertEquals(map.get(StringUtils.fromString("age")), 25L); @@ -161,7 +161,7 @@ public void testFilter() { assertEquals(getType(returns).getTag(), TypeTags.MAP_TAG); BMap map = (BMap) returns; - assertEquals(((BMapType) map.getType()).getConstrainedType().getTag(), TypeTags.INT_TAG); + assertEquals(((MapType) map.getType()).getConstrainedType().getTag(), TypeTags.INT_TAG); assertEquals(map.size(), 2); assertEquals(map.get(StringUtils.fromString("physics")), 75L); assertEquals(map.get(StringUtils.fromString("ict")), 85L); diff --git a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibTableTest.java b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibTableTest.java index 89a71714b5d5..d7658103ba5b 100644 --- a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibTableTest.java +++ b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/LangLibTableTest.java @@ -214,9 +214,6 @@ public void testImplementationrNegativeCases() { validateError(negativeResult, index++, "missing ellipsis token", 181, 38); validateError(negativeResult, index++, "missing open brace token", 181, 38); validateError(negativeResult, index++, "missing close brace token", 181, 39); - validateError(negativeResult, index++, "incompatible types: expected " + - "'table key', " + - "found 'table key(age)'", 182, 9); validateError(negativeResult, index++, "incompatible types: expected '[]', found 'int'", 182, 20); validateError(negativeResult, index++, "table with constraint of type map cannot have key specifier " + "or key type constraint", 188, 30); diff --git a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/TypeParamTest.java b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/TypeParamTest.java index a7dcd6accd70..8b81da85bd3a 100644 --- a/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/TypeParamTest.java +++ b/langlib/langlib-test/src/test/java/org/ballerinalang/langlib/test/TypeParamTest.java @@ -72,9 +72,6 @@ public void testTypeParamNegative() { BAssertUtil.validateError(result, err++, "incompatible types: expected '(int|string)', found 'float'", 131, 24); BAssertUtil.validateError(result, err++, "incompatible types: expected '[int,(int|float)][]', found '[int," + "(int|float|string)][]'", 137, 34); - BAssertUtil.validateError(result, err++, "incompatible types: expected 'function " + - "(ballerina/lang.table:0.0.0:MapType) returns (ballerina/lang.table:0.0.0:MapType1)', " + - "found 'function (other) returns (DataRow)'", 150, 31); BAssertUtil.validateError(result, err++, "unknown type 'dRecord'", 150, 40); BAssertUtil.validateError(result, err++, "missing identifier", 150, 47); BAssertUtil.validateError(result, err++, "unknown type 'x'", 158, 35); 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 d5b54572d3f2..c45a8c9515f2 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 @@ -883,7 +883,7 @@ function testCloneWithTypeDecimalToIntNegative() { var message = err.detail()["message"]; string messageString = message is error ? message.toString() : message.toString(); assert(err.message(), "{ballerina/lang.value}ConversionError"); - assert(messageString, "'decimal' value cannot be converted to 'int'"); + assert(messageString, "'decimal' value '9223372036854775807.5' cannot be converted to 'int'"); decimal[] a1 = [9223372036854775807.5, -9223372036854775807.6]; int[]|error a2e = a1.cloneWithType(IntArray); @@ -892,7 +892,7 @@ function testCloneWithTypeDecimalToIntNegative() { message = err.detail()["message"]; messageString = message is error ? message.toString() : message.toString(); assert(err.message(), "{ballerina/lang.value}ConversionError"); - assert(messageString, "'decimal' value cannot be converted to 'int'"); + assert(messageString, "'decimal' value '9223372036854775807.5' cannot be converted to 'int'"); } type IntSubtypeArray1 int:Signed32[]; @@ -1007,8 +1007,7 @@ function testCloneWithTypeIntArrayToUnionArray() { error err = u; var message = err.detail()["message"]; string messageString = message is error ? message.toString() : message.toString(); - string errMsg = "'int[]' value cannot be converted to '(byte|lang.int:Signed16)[]': " + - "\n\t\tarray element '[2]' should be of type '(byte|lang.int:Signed16)', found '65000'"; + string errMsg = "'int' value cannot be converted to '(byte|lang.int:Signed16)'"; assert(err.message(), "{ballerina/lang.value}ConversionError"); assert(messageString, errMsg); @@ -1739,9 +1738,7 @@ function testCloneWithTypeWithFiniteTypeArrayFromIntArrayNegative() { error err = a; var message = err.detail()["message"]; string messageString = message is error ? message.toString() : message.toString(); - string errMsg = "'int[]' value cannot be converted to 'IntTwoOrThree[]': " + - "\n\t\tarray element '[0]' should be of type 'IntTwoOrThree', found '1'" + - "\n\t\tarray element '[3]' should be of type 'IntTwoOrThree', found '4'"; + string errMsg = "'int' value cannot be converted to 'IntTwoOrThree'"; assert(messageString, errMsg); (IntTwoOrThree|IntThreeOrFour)[]|error c = x.cloneWithType(); @@ -1749,8 +1746,7 @@ function testCloneWithTypeWithFiniteTypeArrayFromIntArrayNegative() { err = c; message = err.detail()["message"]; messageString = message is error ? message.toString() : message.toString(); - errMsg = "'int[]' value cannot be converted to '(IntTwoOrThree|IntThreeOrFour)[]': " + - "\n\t\tarray element '[0]' should be of type '(IntTwoOrThree|IntThreeOrFour)', found '1'"; + errMsg = "'int' value cannot be converted to '(IntTwoOrThree|IntThreeOrFour)'"; assert(messageString, errMsg); int[] y = [3, 4]; @@ -4713,8 +4709,8 @@ function testEnsureTypeJsonToNestedRecordsWithErrors() { Factory|error val = trap clonedJsonVal.ensureType(Factory); error err = val; - string errorMsgPrefix = "incompatible types: 'map<(json & readonly)> & readonly' cannot be cast to 'Factory': "; - string errorMsg = errorMsgPrefix + errorMsgContent; + string errorMsgPrefix = "incompatible types: 'map<(json & readonly)> & readonly' cannot be cast to 'Factory'"; + string errorMsg = errorMsgPrefix; assert(checkpanic err.detail()["message"], errorMsg); assert(err.message(), "{ballerina}TypeCastError"); } diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/config/changeVarType5.json b/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/config/changeVarType5.json index 03d1ad056bb0..39668a65d61d 100644 --- a/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/config/changeVarType5.json +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/config/changeVarType5.json @@ -6,7 +6,7 @@ "source": "changeVarType2.bal", "expected": [ { - "title": "Change variable 'clientResponse' type to 'module1:Response|anydata'", + "title": "Change variable 'clientResponse' type to 'X|module1:ClientError'", "kind": "quickfix", "edits": [ { @@ -20,7 +20,7 @@ "character": 5 } }, - "newText": "module1:Response|anydata" + "newText": "X|module1:ClientError" } ] } diff --git a/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/source/changeVarType2.bal b/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/source/changeVarType2.bal index 8ded0cd20a49..c4c2bc874f88 100644 --- a/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/source/changeVarType2.bal +++ b/language-server/modules/langserver-core/src/test/resources/codeaction/change-var-type/source/changeVarType2.bal @@ -2,5 +2,7 @@ import ballerina/module1; public function main() returns error? { module1:Client clientEP = check new("http://example.com"); - x clientResponse = clientEP->get("/"); + X clientResponse = clientEP->get("/"); } + +type X anydata; diff --git a/language-server/modules/langserver-core/src/test/resources/completion/action_node_context/config/client_remote_action_config1.json b/language-server/modules/langserver-core/src/test/resources/completion/action_node_context/config/client_remote_action_config1.json index 5cbef70cce12..c73e11972bbf 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/action_node_context/config/client_remote_action_config1.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/action_node_context/config/client_remote_action_config1.json @@ -461,10 +461,10 @@ { "label": "message = ...", "kind": "Snippet", - "detail": "message = \"\"", + "detail": "message = ()", "sortText": "AR", "filterText": "message", - "insertText": "message = ${1:\"\"}", + "insertText": "message = ${1:()}", "insertTextFormat": "Snippet" }, { diff --git a/language-server/modules/langserver-core/src/test/resources/completion/expression_context/config/check_expression_ctx_config9.json b/language-server/modules/langserver-core/src/test/resources/completion/expression_context/config/check_expression_ctx_config9.json index 26fd93151e15..534dc2fb67c0 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/expression_context/config/check_expression_ctx_config9.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/expression_context/config/check_expression_ctx_config9.json @@ -104,11 +104,11 @@ { "label": "next()", "kind": "Function", - "detail": "record {|string value; string...;|}|stream:CompletionType", + "detail": "record {|string value;|}|stream:CompletionType", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|string value; string...;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|string value;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" } }, "sortText": "BD", @@ -119,11 +119,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|string value; string...;|}|stream:CompletionType;}", + "detail": "object {public isolated function next() returns record {|string value;|}|stream:CompletionType;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value; string...;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config28.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config28.json index 2f6a4dd238f6..cb47c62a119d 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config28.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config28.json @@ -440,11 +440,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|NodeCredential value; NodeCredential...;|}?;}", + "detail": "object {public isolated function next() returns record {|NodeCredential value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|NodeCredential value; NodeCredential...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|NodeCredential value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config34.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config34.json index 5d1e41158f3a..84151704e5ed 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config34.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_ctx_config34.json @@ -388,11 +388,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int value; int...;|}?;}", + "detail": "object {public isolated function next() returns record {|int value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value; int...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config1.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config1.json index e76bcbf7cd4b..537787e6a6bf 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config1.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config1.json @@ -92,11 +92,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|string value; string...;|}?;}", + "detail": "object {public isolated function next() returns record {|string value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value; string...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config2.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config2.json index a38463e10a92..0487bf79a0ad 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config2.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/field_access_on_method_call_expression_config2.json @@ -92,11 +92,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|string value; string...;|}?;}", + "detail": "object {public isolated function next() returns record {|string value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value; string...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|string value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config10.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config10.json index de3b672ee103..0a1279f71935 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config10.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config10.json @@ -440,11 +440,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int[] value; int[]...;|}?;}", + "detail": "object {public isolated function next() returns record {|int[] value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int[] value; int[]...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int[] value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config14.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config14.json index cbee75c2542a..53b204912b79 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config14.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config14.json @@ -440,11 +440,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|[string, int] value; [string, int]...;|}?;}", + "detail": "object {public isolated function next() returns record {|[string, int] value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|[string, int] value; [string, int]...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|[string, int] value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config17.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config17.json index f3d76f9d08ae..6e605eb203ee 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config17.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config17.json @@ -319,11 +319,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|MyType value; MyType...;|}?;}", + "detail": "object {public isolated function next() returns record {|MyType value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.map:0.0.0_ \n \nReturns an iterator over a map.\n\nThe iterator will iterate over the members of the map not the keys.\nThe function `entries` can be used to iterate over the keys and members together.\nThe function `keys` can be used to iterator over just the keys.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = {\"Carl\": 85, \"Bob\": 50, \"Max\": 60}.iterator();\niterator.next() ⇒ {\"value\":85}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|MyType value; MyType...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `m` \n \n" + "value": "**Package:** _ballerina/lang.map:0.0.0_ \n \nReturns an iterator over a map.\n\nThe iterator will iterate over the members of the map not the keys.\nThe function `entries` can be used to iterate over the keys and members together.\nThe function `keys` can be used to iterator over just the keys.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = {\"Carl\": 85, \"Bob\": 50, \"Max\": 60}.iterator();\niterator.next() ⇒ {\"value\":85}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|MyType value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `m` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config19.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config19.json index 72749ef9da30..eeea3bb1ed93 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config19.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config19.json @@ -130,11 +130,11 @@ { "label": "next()", "kind": "Function", - "detail": "record {|int value; int...;|}|stream:CompletionType", + "detail": "record {|int value;|}|stream:CompletionType", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|int value; int...;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|int value;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" } }, "sortText": "CD", @@ -145,11 +145,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int value; int...;|}|stream:CompletionType;}", + "detail": "object {public isolated function next() returns record {|int value;|}|stream:CompletionType;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value; int...;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config21.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config21.json index 8ae6b56a6b6d..578741b838de 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config21.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config21.json @@ -440,11 +440,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int value; int...;|}?;}", + "detail": "object {public isolated function next() returns record {|int value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value; int...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config22.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config22.json index 42e5b4b6f1c9..d8a184ba6f03 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config22.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config22.json @@ -402,11 +402,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|Report value; Report...;|}?;}", + "detail": "object {public isolated function next() returns record {|Report value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|Report value; Report...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|Report value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config25.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config25.json index e2ee21237d3b..e9f47f7bccf4 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config25.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config25.json @@ -104,11 +104,11 @@ { "label": "next()", "kind": "Function", - "detail": "record {|int value; int...;|}|stream:CompletionType", + "detail": "record {|int value;|}|stream:CompletionType", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|int value; int...;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns the next element in the stream wrapped in a record or () if the stream ends.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nscores.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `record {|int value;|}|stream:CompletionType` \n- If the stream has elements, return the element wrapped in a record with single field called `value`, \notherwise returns () \n \n" } }, "sortText": "CD", @@ -119,11 +119,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int value; int...;|}|stream:CompletionType;}", + "detail": "object {public isolated function next() returns record {|int value;|}|stream:CompletionType;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value; int...;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" + "value": "**Package:** _ballerina/lang.stream:0.0.0_ \n \nReturns an iterator over a stream.\n\n```ballerina\nstream scores = [45, 60, 75, 30, 90].toStream();\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = scores.iterator();\niterator.next() ⇒ {\"value\":45}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value;|}|stream:CompletionType;}` \n- a new iterator object that will iterate over the members of parameter `stm`. \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config9.json b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config9.json index ce58c3e79a5b..a9143384f6ef 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config9.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/field_access_expression_context/config/foreach_stmt_ctx_config9.json @@ -440,11 +440,11 @@ { "label": "iterator()", "kind": "Function", - "detail": "object {public isolated function next() returns record {|int value; int...;|}?;}", + "detail": "object {public isolated function next() returns record {|int value;|}?;}", "documentation": { "right": { "kind": "markdown", - "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value; int...;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" + "value": "**Package:** _ballerina/lang.array:0.0.0_ \n \nReturns an iterator over an array.\n\n```ballerina\nobject {\n public isolated function next() returns record {|int value;|}?;\n} iterator = [2, 4, 6, 8].iterator();\niterator.next() ⇒ {\"value\":2}\n```\n \n \n \n**Return** `object {public isolated function next() returns record {|int value;|}?;}` \n- a new iterator object that will iterate over the members of parameter `arr` \n \n" } }, "sortText": "CD", diff --git a/language-server/modules/langserver-core/src/test/resources/completion/module_const_context/config/config7.json b/language-server/modules/langserver-core/src/test/resources/completion/module_const_context/config/config7.json index acabac212753..85ac8710eae0 100644 --- a/language-server/modules/langserver-core/src/test/resources/completion/module_const_context/config/config7.json +++ b/language-server/modules/langserver-core/src/test/resources/completion/module_const_context/config/config7.json @@ -199,7 +199,7 @@ "value": "" } }, - "sortText": "F", + "sortText": "AB", "insertText": "varName", "insertTextFormat": "Snippet" }, diff --git a/misc/debug-adapter/modules/debug-adapter-runtime/src/main/java/org/ballerinalang/debugadapter/runtime/VariableUtils.java b/misc/debug-adapter/modules/debug-adapter-runtime/src/main/java/org/ballerinalang/debugadapter/runtime/VariableUtils.java index cd15f0d33bf5..870006970d2a 100644 --- a/misc/debug-adapter/modules/debug-adapter-runtime/src/main/java/org/ballerinalang/debugadapter/runtime/VariableUtils.java +++ b/misc/debug-adapter/modules/debug-adapter-runtime/src/main/java/org/ballerinalang/debugadapter/runtime/VariableUtils.java @@ -18,7 +18,7 @@ package org.ballerinalang.debugadapter.runtime; -import io.ballerina.runtime.internal.types.BMapType; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.internal.values.MapValueImpl; /** @@ -46,7 +46,7 @@ public static String getBMapType(Object mapObject) { return String.format(MAP_TYPE_TEMPLATE, UNKNOWN); } - if (!(mapValue.getType() instanceof BMapType type)) { + if (!(mapValue.getType() instanceof MapType type)) { return String.format(MAP_TYPE_TEMPLATE, UNKNOWN); } diff --git a/misc/ls-extensions/modules/bal-shell-service/src/test/java/io/ballerina/shell/service/test/getresult/QueryExpressionsTests.java b/misc/ls-extensions/modules/bal-shell-service/src/test/java/io/ballerina/shell/service/test/getresult/QueryExpressionsTests.java index 8a8530dfdc1a..2bc9e648a35d 100644 --- a/misc/ls-extensions/modules/bal-shell-service/src/test/java/io/ballerina/shell/service/test/getresult/QueryExpressionsTests.java +++ b/misc/ls-extensions/modules/bal-shell-service/src/test/java/io/ballerina/shell/service/test/getresult/QueryExpressionsTests.java @@ -38,7 +38,8 @@ public void testQueryExpressionsWithTables() throws ExecutionException, IOExcept runGetResultTest("query.tables.json"); } - @Test(description = "Test for querying with streams") + // We no longer has fixed names for internal narrowed types so we can't hardcode them + @Test(description = "Test for querying with streams", enabled = false) public void testQueryExpressionsWithStreams() throws ExecutionException, IOException, InterruptedException { runGetResultTest("query.streams.json"); } diff --git a/misc/ls-extensions/modules/bal-shell-service/src/test/resources/testcases/getResult/functional.programming.json b/misc/ls-extensions/modules/bal-shell-service/src/test/resources/testcases/getResult/functional.programming.json index b09f41f282c9..ba9053506774 100644 --- a/misc/ls-extensions/modules/bal-shell-service/src/test/resources/testcases/getResult/functional.programming.json +++ b/misc/ls-extensions/modules/bal-shell-service/src/test/resources/testcases/getResult/functional.programming.json @@ -39,9 +39,9 @@ "source": "f", "result": { "shellValue": { - "value": "function IntFilter", + "value": "function isolated function (int) returns (boolean)", "mimeType":"plain/text", - "type":"IntFilter" + "type":"isolated function (int) returns (boolean)" }, "errors":[], "diagnostics":[], diff --git a/semtypes/build.gradle b/semtypes/build.gradle index b9b3488eafd8..22f85a25bf86 100644 --- a/semtypes/build.gradle +++ b/semtypes/build.gradle @@ -7,6 +7,9 @@ dependencies { } test { + // Add additional system property to distinguish tests requiring all basic types + systemProperty "ballerina.semtype.all.types.test", "true" + useTestNG() { suites 'src/test/resources/testng.xml' } diff --git a/semtypes/spotbugs-exclude.xml b/semtypes/spotbugs-exclude.xml index 97c14919f38e..599e9236ea20 100644 --- a/semtypes/spotbugs-exclude.xml +++ b/semtypes/spotbugs-exclude.xml @@ -21,32 +21,83 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + + - - - + + + + + + + + diff --git a/semtypes/src/main/java/io/ballerina/semtype/ComplexSemType.java b/semtypes/src/main/java/io/ballerina/semtype/ComplexSemType.java deleted file mode 100644 index 09b191eb7c35..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/ComplexSemType.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -import java.util.ArrayList; - -/** - * ComplexSemType node. - * - * @since 2.0.0 - */ -public class ComplexSemType implements SemType { - // For a uniform type with code c, - // all & (1 << c) is non-zero iff this type contains all of the uniform type - // some & (1 << c) is non-zero iff this type contains some but not all of the uniform type - public final UniformTypeBitSet all; - public final UniformTypeBitSet some; - // There is one member of subtypes for each bit set in some. - // Ordered in increasing order of UniformTypeCode - public final SubtypeData[] subtypeDataList; - - public ComplexSemType(UniformTypeBitSet all, UniformTypeBitSet some, SubtypeData[] subtypeDataList) { - this.all = all; - this.some = some; - this.subtypeDataList = subtypeDataList; - } - - public static ComplexSemType createComplexSemType(int allBitset, UniformSubtype... subtypeList) { - int some = 0; - ArrayList dataList = new ArrayList<>(); - for (UniformSubtype uniformSubtype : subtypeList) { - dataList.add(uniformSubtype.subtypeData); - long c = uniformSubtype.uniformTypeCode; - some |= 1L << c; - } - return new ComplexSemType( - new UniformTypeBitSet(allBitset), new UniformTypeBitSet(some), dataList.toArray(new SubtypeData[]{})); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/Env.java b/semtypes/src/main/java/io/ballerina/semtype/Env.java deleted file mode 100644 index ee00c5044853..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/Env.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -import java.util.ArrayList; -import java.util.HashMap; - -/** - * Env node. - * - * @since 2.0.0 - */ -public class Env { - private final HashMap atomTable; - private final ArrayList recListAtoms; - private final ArrayList recMappingAtoms; - private final ArrayList recFunctionAtoms; - - public Env() { - this.atomTable = new HashMap<>(); - // Set up index 0 for use by bddFixReadOnly - this.recListAtoms = new ArrayList<>(); - this.recListAtoms.add(ListAtomicType.LIST_SUBTYPE_RO); - - this.recMappingAtoms = new ArrayList<>(); - // todo: add MAPPING_SUBTYPE_RO - this.recFunctionAtoms = new ArrayList<>(); - } - - -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/FunctionAtomicType.java b/semtypes/src/main/java/io/ballerina/semtype/FunctionAtomicType.java deleted file mode 100644 index 8ca845b74f22..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/FunctionAtomicType.java +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * FunctionAtomicType node. - * - * @since 2.0.0 - */ -public class FunctionAtomicType { - SemType paramType; - SemType retType; -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/ListAtomicType.java b/semtypes/src/main/java/io/ballerina/semtype/ListAtomicType.java deleted file mode 100644 index 6dedbb509453..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/ListAtomicType.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -import java.util.ArrayList; - -/** - * ListAtomicType node. - * - * @since 2.0.0 - */ -public class ListAtomicType implements AtomicType { - final ArrayList members; - final SemType rest; - - public static final ListAtomicType LIST_SUBTYPE_RO = new ListAtomicType(new ArrayList<>(), PredefinedType.READONLY); - - public ListAtomicType(ArrayList members, SemType rest) { - this.members = members; - this.rest = rest; - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/MappingAtomicType.java b/semtypes/src/main/java/io/ballerina/semtype/MappingAtomicType.java deleted file mode 100644 index a455f45c165d..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/MappingAtomicType.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * MappingAtomicType node. - * - * @since 2.0.0 - */ -public class MappingAtomicType implements AtomicType { -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/PredefinedType.java b/semtypes/src/main/java/io/ballerina/semtype/PredefinedType.java deleted file mode 100644 index fcad89624357..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/PredefinedType.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -import io.ballerina.semtype.subtypedata.IntSubtype; - -/** - * Contain predefined types used for constructing other types. - * - * @since 2.0.0 - */ -public final class PredefinedType { - - public static final UniformTypeBitSet NEVER = uniformTypeUnion(0); - public static final UniformTypeBitSet NIL = uniformType(UniformTypeCode.UT_NIL); - public static final UniformTypeBitSet BOOLEAN = uniformType(UniformTypeCode.UT_BOOLEAN); - public static final UniformTypeBitSet INT = uniformType(UniformTypeCode.UT_INT); - public static final UniformTypeBitSet FLOAT = uniformType(UniformTypeCode.UT_FLOAT); - public static final UniformTypeBitSet DECIMAL = uniformType(UniformTypeCode.UT_DECIMAL); - public static final UniformTypeBitSet STRING = uniformType(UniformTypeCode.UT_STRING); - public static final UniformTypeBitSet ERROR = uniformType(UniformTypeCode.UT_ERROR); - public static final UniformTypeBitSet LIST_RW = uniformType(UniformTypeCode.UT_LIST_RW); - public static final UniformTypeBitSet LIST = - uniformTypeUnion((1 << UniformTypeCode.UT_LIST_RO) | (1 << UniformTypeCode.UT_LIST_RW)); - public static final UniformTypeBitSet MAPPING_RW = uniformType(UniformTypeCode.UT_MAPPING_RW); - public static final UniformTypeBitSet MAPPING = - uniformTypeUnion((1 << UniformTypeCode.UT_MAPPING_RO) | (1 << UniformTypeCode.UT_MAPPING_RW)); - - // matches all functions - public static final UniformTypeBitSet FUNCTION = uniformType(UniformTypeCode.UT_FUNCTION); - public static final UniformTypeBitSet TYPEDESC = uniformType(UniformTypeCode.UT_TYPEDESC); - public static final UniformTypeBitSet HANDLE = uniformType(UniformTypeCode.UT_HANDLE); - - public static final UniformTypeBitSet XML = - uniformTypeUnion((1 << UniformTypeCode.UT_XML_RO) | (1 << UniformTypeCode.UT_XML_RW)); - public static final UniformTypeBitSet STREAM = uniformType(UniformTypeCode.UT_STREAM); - public static final UniformTypeBitSet FUTURE = uniformType(UniformTypeCode.UT_FUTURE); - - // this is SubtypeData|error - public static final UniformTypeBitSet TOP = uniformTypeUnion(UniformTypeCode.UT_MASK); - public static final UniformTypeBitSet ANY = - uniformTypeUnion(UniformTypeCode.UT_MASK & ~(1 << UniformTypeCode.UT_ERROR)); - public static final UniformTypeBitSet READONLY = uniformTypeUnion(UniformTypeCode.UT_READONLY); - public static final UniformTypeBitSet SIMPLE_OR_STRING = - uniformTypeUnion((1 << UniformTypeCode.UT_NIL) - | (1 << UniformTypeCode.UT_BOOLEAN) - | (1 << UniformTypeCode.UT_INT) - | (1 << UniformTypeCode.UT_FLOAT) - | (1 << UniformTypeCode.UT_DECIMAL) - | (1 << UniformTypeCode.UT_STRING)); - public static final SemType BYTE = IntSubtype.intWidthUnsigned(8); - - private PredefinedType() { - } - - private static UniformTypeBitSet uniformTypeUnion(int bitset) { - return new UniformTypeBitSet(bitset); - } - - private static UniformTypeBitSet uniformType(int code) { - return new UniformTypeBitSet(1 << code); - } - - public static SemType uniformSubtype(int code, ProperSubtypeData data) { - return ComplexSemType.createComplexSemType(0, new UniformSubtype(code, data)); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/SemTypeMock.java b/semtypes/src/main/java/io/ballerina/semtype/SemTypeMock.java deleted file mode 100644 index f908b14ee024..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/SemTypeMock.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * SemType Interface. - */ -public interface SemTypeMock { - -} - -/** - * Complex SemType implementation. - */ -class ComplexSemTypeMock implements SemTypeMock { - UniformTypeBitSet all; - UniformTypeBitSet some; - -} - -/** - * UniformTypeBitSet SemType implementation. - */ -class UniformTypeBitSetMock implements SemTypeMock { - int value; - - public UniformTypeBitSetMock(int value) { - this.value = value; - } - - public int getValue() { - return value; - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/TypeAtom.java b/semtypes/src/main/java/io/ballerina/semtype/TypeAtom.java deleted file mode 100644 index 1a629bdf616d..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/TypeAtom.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * Represent a TypeAtom. - * - * @since 2.0.0 - */ -public class TypeAtom implements Atom { - long index; - AtomicType atomicType; - - public TypeAtom(long index, AtomicType atomicType) { - this.index = index; - this.atomicType = atomicType; - } - - public static TypeAtom createTypeAtom(long index, AtomicType atomicType) { - return new TypeAtom(index, atomicType); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/TypeCheckContext.java b/semtypes/src/main/java/io/ballerina/semtype/TypeCheckContext.java deleted file mode 100644 index ec5acf707f68..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/TypeCheckContext.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * TypeCheckContext node. - * - * @since 2.0.0 - */ -public class TypeCheckContext { - private final Env env; - // todo: Normal hash tables should do here - // BddMemoTable listMemo = table []; - // BddMemoTable mappingMemo = table []; - // BddMemoTable functionMemo = table []; - - public TypeCheckContext(Env env) { - this.env = env; - } - - -// function listAtomType(Atom atom) returns ListAtomicType { -// if atom is RecAtom { -// return self.env.getRecListAtomType(atom); -// } -// else { -// return atom.atomicType; -// } -// } -// -// function mappingAtomType(Atom atom) returns MappingAtomicType { -// if atom is RecAtom { -// return self.env.getRecMappingAtomType(atom); -// } -// else { -// return atom.atomicType; -// } -// } -// -// function functionAtomType(Atom atom) returns FunctionAtomicType { -// return self.env.getRecFunctionAtomType(atom); -// } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeCode.java b/semtypes/src/main/java/io/ballerina/semtype/UniformTypeCode.java deleted file mode 100644 index 2b3adf472510..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeCode.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * Represent bit field that indicate which uniform type a semType belongs to. - * Regular types are divided longo mutable part and immutable part and these parts are called an uniform type. - * 5th bit indicate mutability; 0 immutable, 1 mutable. - * - * @since 2.0.0 - */ -public final class UniformTypeCode { - - // Inherently immutable - public static final int UT_NIL = 0x00; - public static final int UT_BOOLEAN = 0x01; - - // Selectively immutable; immutable half - public static final int UT_LIST_RO = 0x02; - public static final int UT_MAPPING_RO = 0x03; - public static final int UT_TABLE_RO = 0x04; - public static final int UT_XML_RO = 0x05; - public static final int UT_OBJECT_RO = 0x06; - - // Rest of inherently immutable - public static final int UT_INT = 0x07; - public static final int UT_FLOAT = 0x08; - public static final int UT_DECIMAL = 0x09; - public static final int UT_STRING = 0x0A; - public static final int UT_ERROR = 0x0B; - public static final int UT_FUNCTION = 0x0C; - public static final int UT_TYPEDESC = 0x0D; - public static final int UT_HANDLE = 0x0E; - - // Inherently mutable - public static final int UT_FUTURE = 0x10; - public static final int UT_STREAM = 0x11; - - // Selectively immutable; mutable half - public static final int UT_LIST_RW = 0x12; - public static final int UT_MAPPING_RW = 0x13; - public static final int UT_TABLE_RW = 0x14; - public static final int UT_XML_RW = 0x15; - public static final int UT_OBJECT_RW = 0x16; - - // Helper bit fields (does not represent uniform type tag) - static final int UT_COUNT = UT_OBJECT_RW + 1; - static final int UT_MASK = (1 << UT_COUNT) - 1; - - static final int UT_COUNT_RO = 0x10; - static final int UT_READONLY = (1 << UT_COUNT_RO) - 1; - - static final int UT_RW_MASK = UT_MASK ^ ~UT_READONLY; - - private UniformTypeCode() { - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeOps.java b/semtypes/src/main/java/io/ballerina/semtype/UniformTypeOps.java deleted file mode 100644 index 2fb53b153b62..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeOps.java +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -/** - * Interface representing type operations on uniform types. - * - * @since 2.0.0 - */ -public interface UniformTypeOps extends IsEmptyOp, CommonUniformTypeOps { -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/definition/FunctionDefinition.java b/semtypes/src/main/java/io/ballerina/semtype/definition/FunctionDefinition.java deleted file mode 100644 index 34028895f964..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/definition/FunctionDefinition.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.definition; - -import io.ballerina.semtype.Definition; -import io.ballerina.semtype.Env; -import io.ballerina.semtype.SemType; - -/** - * Represent function type desc. - * - * @since 2.0.0 - */ -public class FunctionDefinition implements Definition { - @Override - public SemType getSemType(Env env) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/definition/ListDefinition.java b/semtypes/src/main/java/io/ballerina/semtype/definition/ListDefinition.java deleted file mode 100644 index 3a277c03c12b..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/definition/ListDefinition.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.definition; - -import io.ballerina.semtype.Definition; -import io.ballerina.semtype.Env; -import io.ballerina.semtype.SemType; - -/** - * Represent list/tuple type desc. - * - * @since 2.0.0 - */ -public class ListDefinition implements Definition { - @Override - public SemType getSemType(Env env) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/definition/MappingDefinition.java b/semtypes/src/main/java/io/ballerina/semtype/definition/MappingDefinition.java deleted file mode 100644 index d9651e97a3b8..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/definition/MappingDefinition.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.definition; - -import io.ballerina.semtype.Definition; -import io.ballerina.semtype.Env; -import io.ballerina.semtype.SemType; - -/** - * Represent mapping type desc. - * - * @since 2.0.0 - */ -public class MappingDefinition implements Definition { - @Override - public SemType getSemType(Env env) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/FunctionOps.java b/semtypes/src/main/java/io/ballerina/semtype/typeops/FunctionOps.java deleted file mode 100644 index 507e271c1ed9..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/FunctionOps.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.typeops; - -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.TypeCheckContext; -import io.ballerina.semtype.UniformTypeOps; - -/** - * Function specific methods operate on SubtypeData. - * - * @since 2.0.0 - */ -public class FunctionOps extends CommonOps implements UniformTypeOps { - @Override - public boolean isEmpty(TypeCheckContext tc, SubtypeData t) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRWOps.java b/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRWOps.java deleted file mode 100644 index d6734164f8d4..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRWOps.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.typeops; - -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.TypeCheckContext; -import io.ballerina.semtype.UniformTypeOps; - -/** - * List read/write specific methods operate on SubtypeData. - * - * @since 2.0.0 - */ -public class ListTypeRWOps extends CommonOps implements UniformTypeOps { - @Override - public boolean isEmpty(TypeCheckContext tc, SubtypeData t) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRoOps.java b/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRoOps.java deleted file mode 100644 index 9ee27e5105e9..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/ListTypeRoOps.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.typeops; - -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.TypeCheckContext; -import io.ballerina.semtype.UniformTypeOps; - -/** - * List readonly specific methods operate on SubtypeData. - * - * @since 2.0.0 - */ -public class ListTypeRoOps extends CommonOps implements UniformTypeOps { - @Override - public boolean isEmpty(TypeCheckContext tc, SubtypeData t) { - throw new AssertionError(); - } -} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingCommonOps.java b/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingCommonOps.java deleted file mode 100644 index 055ccb15133f..000000000000 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/MappingCommonOps.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype.typeops; - -import io.ballerina.semtype.UniformTypeOps; - -// todo: use this to place common things between Ro and RW, if there are non; delete this. -/** - * Common mapping related methods operate on SubtypeData. - * - * @since 2.0.0 - */ -public abstract class MappingCommonOps extends CommonOps implements UniformTypeOps { - -} diff --git a/semtypes/src/main/java/io/ballerina/types/Atom.java b/semtypes/src/main/java/io/ballerina/types/Atom.java new file mode 100644 index 000000000000..b1e053887d11 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Atom.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Represent the BDD atom. + * + * @since 2201.8.0 + */ +public interface Atom { + + /** + * Get the unique index of the atom. + */ + int index(); + + /** + * Get the kind of the atom. + */ + Kind kind(); + + /** + * This method returns a unique identifier for an Atom. + * The identifier is a combination of the atom's index and kind. + * + * @return AtomIdentifier - a record containing the index and kind of the atom. + */ + default AtomIdentifier getIdentifier() { + return new AtomIdentifier(index(), kind()); + } + + record AtomIdentifier(int index, Kind kind) { + } + + enum Kind { + LIST_ATOM, + FUNCTION_ATOM, + MAPPING_ATOM, + CELL_ATOM, + XML_ATOM, + DISTINCT_ATOM + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/AtomicType.java b/semtypes/src/main/java/io/ballerina/types/AtomicType.java similarity index 68% rename from semtypes/src/main/java/io/ballerina/semtype/AtomicType.java rename to semtypes/src/main/java/io/ballerina/types/AtomicType.java index 3eb7f7377e11..7de1e82a3867 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/AtomicType.java +++ b/semtypes/src/main/java/io/ballerina/types/AtomicType.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,18 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Represent AtomicType. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface AtomicType { + + Atom.Kind atomKind(); } diff --git a/semtypes/src/main/java/io/ballerina/types/BasicSubtype.java b/semtypes/src/main/java/io/ballerina/types/BasicSubtype.java new file mode 100644 index 000000000000..3fd190893077 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BasicSubtype.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * BasicSubtype node. + * + * @since 2201.8.0 + */ +public class BasicSubtype { + public final BasicTypeCode basicTypeCode; + public final ProperSubtypeData subtypeData; + + private BasicSubtype(BasicTypeCode basicTypeCode, ProperSubtypeData properSubtypeData) { + this.basicTypeCode = basicTypeCode; + this.subtypeData = properSubtypeData; + } + + public static BasicSubtype from(BasicTypeCode typeCode, ProperSubtypeData data) { + return new BasicSubtype(typeCode, data); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/BasicTypeBitSet.java b/semtypes/src/main/java/io/ballerina/types/BasicTypeBitSet.java new file mode 100644 index 000000000000..3d14765053c0 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BasicTypeBitSet.java @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * BasicTypeBitSet node. + * + * @since 2201.8.0 + */ +public final class BasicTypeBitSet implements SemType { + + public final int bitset; + + private BasicTypeBitSet(int bitset) { + this.bitset = bitset; + } + + public static BasicTypeBitSet from(int bitset) { + if (bitset == 0) { + return BitSetCache.ZERO; + } + if (Integer.bitCount(bitset) == 1) { + return BitSetCache.CACHE[Integer.numberOfTrailingZeros(bitset)]; + } + return new BasicTypeBitSet(bitset); + } + + public static BasicTypeBitSet union(BasicTypeBitSet t1, BasicTypeBitSet t2) { + return BasicTypeBitSet.from(t1.bitset | t2.bitset); + } + + @Override + public String toString() { + return PredefinedType.toString(this.bitset); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof BasicTypeBitSet b) { + return b.bitset == this.bitset; + } + return false; + } + + @Override + public int hashCode() { + return bitset; + } + + @Override + public int all() { + return bitset; + } + + private static final class BitSetCache { + + private static final int SIZE = 0x14; + private static final BasicTypeBitSet[] CACHE = new BasicTypeBitSet[SIZE]; + private static final BasicTypeBitSet ZERO = new BasicTypeBitSet(0); + + static { + for (int i = 0; i < SIZE; i++) { + CACHE[i] = new BasicTypeBitSet(1 << i); + } + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/BasicTypeCode.java b/semtypes/src/main/java/io/ballerina/types/BasicTypeCode.java new file mode 100644 index 000000000000..d614491fd6c8 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BasicTypeCode.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.lang.reflect.Field; +import java.util.HashMap; +import java.util.Map; + +/** + * Represent bit field that indicate which basic type a semType belongs to. + * + * @since 2201.10.0 + */ +public class BasicTypeCode { + // Inherently immutable + public static final BasicTypeCode BT_NIL = from(0x00); + public static final BasicTypeCode BT_BOOLEAN = from(0x01); + public static final BasicTypeCode BT_INT = from(0x02); + public static final BasicTypeCode BT_FLOAT = from(0x03); + public static final BasicTypeCode BT_DECIMAL = from(0x04); + public static final BasicTypeCode BT_STRING = from(0x05); + public static final BasicTypeCode BT_ERROR = from(0x06); + public static final BasicTypeCode BT_TYPEDESC = from(0x07); + public static final BasicTypeCode BT_HANDLE = from(0x08); + public static final BasicTypeCode BT_FUNCTION = from(0x09); + public static final BasicTypeCode BT_REGEXP = from(0x0A); + + // Inherently mutable + public static final BasicTypeCode BT_FUTURE = from(0x0B); + public static final BasicTypeCode BT_STREAM = from(0x0C); + + // Selectively immutable + public static final BasicTypeCode BT_LIST = from(0x0D); + public static final BasicTypeCode BT_MAPPING = from(0x0E); + public static final BasicTypeCode BT_TABLE = from(0x0F); + public static final BasicTypeCode BT_XML = from(0x10); + public static final BasicTypeCode BT_OBJECT = from(0x11); + + // Non-val + public static final BasicTypeCode BT_CELL = from(0x12); + public static final BasicTypeCode BT_UNDEF = from(0x13); + + // Helper bit fields (does not represent basic type tag) + static final int VT_COUNT = BT_OBJECT.code + 1; + static final int VT_MASK = (1 << VT_COUNT) - 1; + + static final int VT_COUNT_INHERENTLY_IMMUTABLE = BT_FUTURE.code; + public static final int VT_INHERENTLY_IMMUTABLE = (1 << VT_COUNT_INHERENTLY_IMMUTABLE) - 1; + + public final int code; + + // There is an integer for each basic type. + private BasicTypeCode(int code) { + this.code = code; + } + + public static BasicTypeCode from(int code) { + // todo: Add validation + return new BasicTypeCode(code); + } + + // Only used for .toString() method to aid debugging. + private static Map fieldNames = new HashMap<>(); + static { + for (Field field : BasicTypeCode.class.getDeclaredFields()) { + if (field.getType() == BasicTypeCode.class) { + try { + BasicTypeCode o = (BasicTypeCode) field.get(null); + fieldNames.put(o.code, field.getName()); + } catch (IllegalAccessException e) { + throw new IllegalStateException(); + } + } + } + } + + @Override + public String toString() { + return fieldNames.get(this.code); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/BasicTypeOps.java b/semtypes/src/main/java/io/ballerina/types/BasicTypeOps.java new file mode 100644 index 000000000000..71dcaf7b01fe --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BasicTypeOps.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Interface representing type operations on basic types. + * + * @since 2201.8.0 + */ +public interface BasicTypeOps extends IsEmptyOp, CommonBasicTypeOps { +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/Bdd.java b/semtypes/src/main/java/io/ballerina/types/Bdd.java similarity index 69% rename from semtypes/src/main/java/io/ballerina/semtype/Bdd.java rename to semtypes/src/main/java/io/ballerina/types/Bdd.java index 351df65a953b..a51aa98545b7 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/Bdd.java +++ b/semtypes/src/main/java/io/ballerina/types/Bdd.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,16 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Represent BDD node. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface Bdd extends ProperSubtypeData { } diff --git a/semtypes/src/main/java/io/ballerina/types/BddMemo.java b/semtypes/src/main/java/io/ballerina/types/BddMemo.java new file mode 100644 index 000000000000..c9b82a59ac3b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BddMemo.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Represent BddMomo type used for memoization. + * + * @since 2201.8.0 + */ +public class BddMemo { + + protected MemoStatus isEmpty; + + public BddMemo() { + this.isEmpty = MemoStatus.NULL; + } + + public void setIsEmpty(boolean isEmpty) { + this.isEmpty = isEmpty ? MemoStatus.TRUE : MemoStatus.FALSE; + } + + public boolean isEmpty() { + return this.isEmpty == MemoStatus.TRUE; + } + /** + * Represent if BddMemo is null or not. + * + * @since 3.0.0 + */ + public enum MemoStatus { + LOOP, TRUE, FALSE, CYCLIC, PROVISIONAL, NULL; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/BddPath.java b/semtypes/src/main/java/io/ballerina/types/BddPath.java new file mode 100644 index 000000000000..4eceb36065f0 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/BddPath.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.typeops.BddCommonOps; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents path from root to leaf (ending with true). + * bdd gets the Bdd for this path + * + * @since 2201.8.0 + */ +public class BddPath { + Bdd bdd; + List pos; + List neg; + + private BddPath(BddPath bddPath) { + this.bdd = bddPath.bdd; + this.pos = new ArrayList<>(bddPath.pos); // pos: path.pos.clone() + this.neg = new ArrayList<>(bddPath.neg); // pos: path.pos.clone() + } + + public BddPath() { + this.bdd = BddAllOrNothing.bddAll(); + this.pos = new ArrayList<>(); + this.neg = new ArrayList<>(); + } + + public static void bddPaths(Bdd b, List paths, BddPath accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + paths.add(accum); + } + } else { + BddPath left = bddPathClone(accum); + BddPath right = bddPathClone(accum); + BddNode bn = (BddNode) b; + left.pos.add(bn.atom()); + left.bdd = BddCommonOps.bddIntersect(left.bdd, BddCommonOps.bddAtom(bn.atom())); + bddPaths(bn.left(), paths, left); + bddPaths(bn.middle(), paths, accum); + right.neg.add(bn.atom()); + right.bdd = BddCommonOps.bddDiff(right.bdd, BddCommonOps.bddAtom(bn.atom())); + bddPaths(bn.right(), paths, right); + } + } + + private static BddPath bddPathClone(BddPath path) { + return new BddPath(path); + } + + public static BddPath from() { + return new BddPath(); + } + +} diff --git a/semtypes/src/main/java/io/ballerina/types/CellAtomicType.java b/semtypes/src/main/java/io/ballerina/types/CellAtomicType.java new file mode 100644 index 000000000000..a17744fa2625 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/CellAtomicType.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * CellAtomicType node. + * + * @param ty Type "wrapped" by this cell + * @param mut Mutability of the cell + * @since 2201.10.0 + */ +public record CellAtomicType(SemType ty, CellMutability mut) implements AtomicType { + + public CellAtomicType { + assert ty != null; + } + + public static CellAtomicType from(SemType ty, CellMutability mut) { + assert ty != null; + // TODO: return final fields where applicable + return new CellAtomicType(ty, mut); + } + + @Override + public Atom.Kind atomKind() { + return Atom.Kind.CELL_ATOM; + } + + public enum CellMutability { + CELL_MUT_NONE, + CELL_MUT_LIMITED, + CELL_MUT_UNLIMITED + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/CellSemType.java b/semtypes/src/main/java/io/ballerina/types/CellSemType.java new file mode 100644 index 000000000000..d799bfcd193f --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/CellSemType.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * This is to represent a SemType belonging to cell basic type. + * + * @since 2201.10.0 + */ +public class CellSemType implements ComplexSemType { + + // Holding on to the single value instead of the array with a single value is more memory efficient. However, if + // this start to cause problems in the future, we can change this to an array. + private final ProperSubtypeData subtypeData; + + private CellSemType(ProperSubtypeData[] subtypeDataList) { + assert subtypeDataList.length == 1; + this.subtypeData = subtypeDataList[0]; + } + + public static CellSemType from(ProperSubtypeData[] subtypeDataList) { + return new CellSemType(subtypeDataList); + } + + @Override + public String toString() { + return "CellSemType{" + subtypeDataList()[0] + '}'; + } + + @Override + public int all() { + return 0; + } + + @Override + public int some() { + return PredefinedType.CELL.bitset; + } + + @Override + public ProperSubtypeData[] subtypeDataList() { + return new ProperSubtypeData[]{subtypeData}; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/CombinedRange.java b/semtypes/src/main/java/io/ballerina/types/CombinedRange.java new file mode 100644 index 000000000000..f097004b2220 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/CombinedRange.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.Range; + +/** + * Represents a combined range. + * + * @param range range + * @param i1 i1 + * @param i2 i2 + * @since 2201.11.0 + */ +public record CombinedRange(Range range, Long i1, Long i2) { + + public static CombinedRange from(Range range, Long i1, Long i2) { + return new CombinedRange(range, i1, i2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/Common.java b/semtypes/src/main/java/io/ballerina/types/Common.java new file mode 100644 index 000000000000..0a253308c48e --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Common.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.BiFunction; + +import static io.ballerina.types.Conjunction.and; +import static io.ballerina.types.typeops.BddCommonOps.bddComplement; +import static io.ballerina.types.typeops.BddCommonOps.bddDiff; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; +import static io.ballerina.types.typeops.BddCommonOps.bddUnion; + +/** + * Code common to implementation of multiple basic types. + * + * @since 2201.8.0 + */ +public class Common { + + // [from nballerina] A Bdd represents a disjunction of conjunctions of atoms, where each atom is either positive or + // negative (negated). Each path from the root to a leaf that is true represents one of the conjunctions + // We walk the tree, accumulating the positive and negative conjunctions for a path as we go. + // When we get to a leaf that is true, we apply the predicate to the accumulated conjunctions. + + public static boolean bddEvery(Context cx, + Bdd b, + Conjunction pos, + Conjunction neg, + BddPredicate predicate) { + if (b instanceof BddAllOrNothing allOrNothing) { + return !allOrNothing.isAll() || predicate.apply(cx, pos, neg); + } else { + BddNode bn = (BddNode) b; + return bddEvery(cx, bn.left(), and(bn.atom(), pos), neg, predicate) + && bddEvery(cx, bn.middle(), pos, neg, predicate) + && bddEvery(cx, bn.right(), pos, and(bn.atom(), neg), predicate); + } + } + + public static boolean bddEveryPositive(Context cx, Bdd b, Conjunction pos, Conjunction neg, + BddPredicate predicate) { + if (b instanceof BddAllOrNothing allOrNothing) { + return !allOrNothing.isAll() || predicate.apply(cx, pos, neg); + } else { + BddNode bn = (BddNode) b; + return bddEveryPositive(cx, bn.left(), andIfPositive(bn.atom(), pos), neg, predicate) + && bddEveryPositive(cx, bn.middle(), pos, neg, predicate) + && bddEveryPositive(cx, bn.right(), pos, andIfPositive(bn.atom(), neg), predicate); + } + } + + public static Conjunction andIfPositive(Atom atom, Conjunction next) { + if (atom instanceof RecAtom recAtom && recAtom.index < 0) { + return next; + } + return and(atom, next); + } + + public static boolean bddPosMaybeEmpty(Bdd b) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll(); + } else { + BddNode bddNode = (BddNode) b; + return bddPosMaybeEmpty(bddNode.middle()) || bddPosMaybeEmpty(bddNode.right()); + } + } + + public static SubtypeData bddSubtypeUnion(SubtypeData t1, SubtypeData t2) { + return bddUnion((Bdd) t1, (Bdd) t2); + } + + public static SubtypeData bddSubtypeIntersect(SubtypeData t1, SubtypeData t2) { + return bddIntersect((Bdd) t1, (Bdd) t2); + } + + public static SubtypeData bddSubtypeDiff(SubtypeData t1, SubtypeData t2) { + return bddDiff((Bdd) t1, (Bdd) t2); + } + + public static SubtypeData bddSubtypeComplement(SubtypeData t) { + return bddComplement((Bdd) t); + } + + public static SemType[] shallowCopyTypes(SemType[] v) { + return Arrays.copyOf(v, v.length); + } + + public static CellSemType[] shallowCopyCellTypes(CellSemType[] v) { + return shallowCopyCellTypes(v, v.length); + } + + public static CellSemType[] shallowCopyCellTypes(CellSemType[] v, int newLength) { + return Arrays.copyOf(v, newLength); + } + + public static List shallowCopyTypes(List v) { + return new ArrayList<>(v); + } + + public static String[] shallowCopyStrings(String[] v, int newLength) { + return Arrays.copyOf(v, newLength); + } + + public static boolean notIsEmpty(Context cx, SubtypeData d) { + return false; + } + + // Returns whether s1.codePoints < s2.codePoints + public static boolean codePointCompare(String s1, String s2) { + if (s1.equals(s2)) { + return false; + } + int len1 = s1.length(); + int len2 = s2.length(); + if (len1 < len2 && s2.substring(0, len1).equals(s1)) { + return true; + } + int cpCount1 = s1.codePointCount(0, len1); + int cpCount2 = s2.codePointCount(0, len2); + for (int cp = 0; cp < cpCount1 && cp < cpCount2;) { + int codepoint1 = s1.codePointAt(cp); + int codepoint2 = s2.codePointAt(cp); + if (codepoint1 == codepoint2) { + cp++; + continue; + } + return codepoint1 < codepoint2; + } + return false; + } + + + public static boolean isNothingSubtype(SubtypeData data) { + return data instanceof AllOrNothingSubtype allOrNothingSubtype && allOrNothingSubtype.isNothingSubtype(); + } + + /** + * Function interface used for method references. + * + * @since 3.0.0 + */ + public interface BddPredicate { + boolean apply(Context cx, Conjunction posList, Conjunction negList); + } + + public interface BddIsEmptyPredicate extends BiFunction { + + } + + public static boolean memoSubtypeIsEmpty(Context cx, Map memoTable, + BddIsEmptyPredicate isEmptyPredicate, Bdd b) { + BddMemo mm = memoTable.get(b); + BddMemo m; + if (mm != null) { + BddMemo.MemoStatus res = mm.isEmpty; + switch (res) { + case CYCLIC: + // Since we define types inductively we consider these to be empty + return true; + case TRUE, FALSE: + // We know whether b is empty or not for certain + return res == BddMemo.MemoStatus.TRUE; + case NULL: + // this is same as not having memo so fall through + m = mm; + break; + case LOOP, PROVISIONAL: + // We've got a loop. + mm.isEmpty = BddMemo.MemoStatus.LOOP; + return true; + default: + throw new AssertionError("Unexpected memo status: " + res); + } + } else { + m = new BddMemo(); + memoTable.put(b, m); + } + m.isEmpty = BddMemo.MemoStatus.PROVISIONAL; + int initStackDepth = cx.memoStack.size(); + cx.memoStack.add(m); + boolean isEmpty = isEmptyPredicate.apply(cx, b); + boolean isLoop = m.isEmpty == BddMemo.MemoStatus.LOOP; + if (!isEmpty || initStackDepth == 0) { + for (int i = initStackDepth + 1; i < cx.memoStack.size(); i++) { + BddMemo.MemoStatus memoStatus = cx.memoStack.get(i).isEmpty; + if (Objects.requireNonNull(memoStatus) == BddMemo.MemoStatus.PROVISIONAL || + memoStatus == BddMemo.MemoStatus.LOOP || memoStatus == BddMemo.MemoStatus.CYCLIC) { + cx.memoStack.get(i).isEmpty = isEmpty ? BddMemo.MemoStatus.TRUE : BddMemo.MemoStatus.NULL; + } + } + while (cx.memoStack.size() > initStackDepth) { + cx.memoStack.subList(initStackDepth, cx.memoStack.size()).clear(); + } + // The only way that we have found that this can be empty is by going through a loop. + // This means that the shapes in the type would all be infinite. + // But we define types inductively, which means we only consider finite shapes. + if (isLoop && isEmpty) { + m.isEmpty = BddMemo.MemoStatus.CYCLIC; + } else { + m.isEmpty = isEmpty ? BddMemo.MemoStatus.TRUE : BddMemo.MemoStatus.FALSE; + } + } + return isEmpty; + } + + public static boolean isAllSubtype(SubtypeData d) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + return false; + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/CommonUniformTypeOps.java b/semtypes/src/main/java/io/ballerina/types/CommonBasicTypeOps.java similarity index 72% rename from semtypes/src/main/java/io/ballerina/semtype/CommonUniformTypeOps.java rename to semtypes/src/main/java/io/ballerina/types/CommonBasicTypeOps.java index bc7cd3067b89..2739e376f5eb 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/CommonUniformTypeOps.java +++ b/semtypes/src/main/java/io/ballerina/types/CommonBasicTypeOps.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,18 +11,18 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Operations common to most of the subtypes. * - * @since 2.0.0 + * @since 2201.8.0 */ -public interface CommonUniformTypeOps { +public interface CommonBasicTypeOps { SubtypeData union(SubtypeData t1, SubtypeData t2); SubtypeData intersect(SubtypeData t1, SubtypeData t2); SubtypeData diff(SubtypeData t1, SubtypeData t2); diff --git a/semtypes/src/main/java/io/ballerina/types/ComplexSemType.java b/semtypes/src/main/java/io/ballerina/types/ComplexSemType.java new file mode 100644 index 000000000000..9a72477dad31 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/ComplexSemType.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import static io.ballerina.types.BasicTypeCode.BT_CELL; + +/** + * ComplexSemType node. + * + * @since 2201.8.0 + */ +public interface ComplexSemType extends SemType { + + static ComplexSemType createComplexSemType(int allBitset, BasicSubtype... subtypeList) { + return createComplexSemType(allBitset, Arrays.asList(subtypeList)); + } + + static ComplexSemType createComplexSemType(int allBitset, int someBitset, ProperSubtypeData[] subtypeData) { + if (allBitset == 0 && someBitset == (1 << BT_CELL.code)) { + return CellSemType.from(subtypeData); + } + return new ComplexSemTypeImpl(allBitset, someBitset, subtypeData); + } + + static ComplexSemType createComplexSemType(int allBitset, List subtypeList) { + int some = 0; + ArrayList dataList = new ArrayList<>(); + for (BasicSubtype basicSubtype : subtypeList) { + dataList.add(basicSubtype.subtypeData); + int c = basicSubtype.basicTypeCode.code; + some |= 1 << c; + } + return createComplexSemType(allBitset, some, dataList.toArray(ProperSubtypeData[]::new)); + } + + int all(); + + int some(); + + ProperSubtypeData[] subtypeDataList(); +} diff --git a/semtypes/src/main/java/io/ballerina/types/ComplexSemTypeImpl.java b/semtypes/src/main/java/io/ballerina/types/ComplexSemTypeImpl.java new file mode 100644 index 000000000000..d280d98a7081 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/ComplexSemTypeImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.util.Arrays; +import java.util.Objects; + +/** + * @param all all & (1 << c) is non-zero iff this type contains all the basic type with code c + * @param some some & (1 << c) is non-zero iff this type contains some but not all the basic type with code c + * @param subtypeDataList There is one member of subtypes for each bit set in some. Ordered in increasing order of + * BasicTypeCode + */ +record ComplexSemTypeImpl(int all, int some, ProperSubtypeData[] subtypeDataList) implements ComplexSemType { + + @Override + public String toString() { + return "ComplexSemType{all=" + all + PredefinedType.toString(all) + ", some=" + some() + + PredefinedType.toString(some) + ", subtypeDataList=" + Arrays.toString(subtypeDataList()) + '}'; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof ComplexSemType other)) { + return false; + } + return Objects.equals(all(), other.all()) && + Objects.equals(some(), other.some()) && + Arrays.equals(subtypeDataList(), other.subtypeDataList()); + } + + @Override + public int hashCode() { + return Objects.hash(all(), some(), Arrays.hashCode(subtypeDataList())); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/Conjunction.java b/semtypes/src/main/java/io/ballerina/types/Conjunction.java new file mode 100644 index 000000000000..84b524c1e7ef --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Conjunction.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Represents the Conjunction record type. + * + * @since 2201.8.0 + */ +public class Conjunction { + public Atom atom; + public Conjunction next; + + private Conjunction(Atom atom, Conjunction next) { + this.atom = atom; + this.next = next; + } + + public static Conjunction and(Atom atom, Conjunction next) { + return new Conjunction(atom, next); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/Context.java b/semtypes/src/main/java/io/ballerina/types/Context.java new file mode 100644 index 000000000000..d0da307ccf5c --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Context.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * TypeCheckContext node. + * + * @since 2201.8.0 + */ +public final class Context { + + public final Env env; + public final Map functionMemo = new HashMap<>(); + public final Map listMemo = new HashMap<>(); + public final Map mappingMemo = new HashMap<>(); + public final Map comparableMemo = new HashMap<>(); + + // Contains all BddMemo entries with isEmpty == PROVISIONAL + final List memoStack = new ArrayList<>(); + + private static volatile Context instance; + + SemType anydataMemo; + SemType jsonMemo; + SemType cloneableMemo; + SemType isolatedObjectMemo; + SemType serviceObjectMemo; + + private Context(Env env) { + this.env = env; + } + + public static Context from(Env env) { + if (instance == null) { + synchronized (Context.class) { + if (instance == null) { + instance = new Context(env); + } + } + } + if (instance.env == env) { + return instance; + } else { + instance = new Context(env); + } + return instance; + } + + public ListAtomicType listAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecListAtomType(recAtom); + } else { + return (ListAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public MappingAtomicType mappingAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecMappingAtomType(recAtom); + } else { + return (MappingAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public FunctionAtomicType functionAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return this.env.getRecFunctionAtomType(recAtom); + } else { + return (FunctionAtomicType) ((TypeAtom) atom).atomicType(); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/Core.java b/semtypes/src/main/java/io/ballerina/types/Core.java new file mode 100644 index 000000000000..82ab6e91c296 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Core.java @@ -0,0 +1,1001 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.definition.ListDefinition; +import io.ballerina.types.definition.MappingDefinition; +import io.ballerina.types.definition.ObjectDefinition; +import io.ballerina.types.definition.ObjectQualifiers; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.BddNodeImpl; +import io.ballerina.types.subtypedata.BddNodeSimple; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.StringSubtype; +import io.ballerina.types.subtypedata.TableSubtype; +import io.ballerina.types.typeops.SubtypePair; +import io.ballerina.types.typeops.SubtypePairs; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import static io.ballerina.types.BasicTypeCode.BT_BOOLEAN; +import static io.ballerina.types.BasicTypeCode.BT_CELL; +import static io.ballerina.types.BasicTypeCode.BT_DECIMAL; +import static io.ballerina.types.BasicTypeCode.BT_FLOAT; +import static io.ballerina.types.BasicTypeCode.BT_INT; +import static io.ballerina.types.BasicTypeCode.BT_LIST; +import static io.ballerina.types.BasicTypeCode.BT_MAPPING; +import static io.ballerina.types.BasicTypeCode.BT_NIL; +import static io.ballerina.types.BasicTypeCode.BT_STRING; +import static io.ballerina.types.BasicTypeCode.VT_MASK; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.Common.isNothingSubtype; +import static io.ballerina.types.PredefinedType.CELL_ATOMIC_VAL; +import static io.ballerina.types.PredefinedType.INNER; +import static io.ballerina.types.PredefinedType.LIST; +import static io.ballerina.types.PredefinedType.LIST_ATOMIC_INNER; +import static io.ballerina.types.PredefinedType.MAPPING; +import static io.ballerina.types.PredefinedType.MAPPING_ATOMIC_INNER; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.REGEXP; +import static io.ballerina.types.PredefinedType.SIMPLE_OR_STRING; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.PredefinedType.VAL; +import static io.ballerina.types.PredefinedType.VAL_READONLY; +import static io.ballerina.types.PredefinedType.XML; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; +import static io.ballerina.types.typeops.CellOps.intersectCellAtomicType; +import static io.ballerina.types.typeops.ListOps.bddListMemberTypeInnerVal; +import static io.ballerina.types.typeops.MappingOps.bddMappingMemberTypeInner; +import static java.lang.Long.MAX_VALUE; +import static java.lang.Long.MIN_VALUE; + +/** + * Contain functions defined in `core.bal` file. + * + * @since 2201.8.0 + */ +public final class Core { + + public static CellAtomicType cellAtomType(Atom atom) { + return (CellAtomicType) ((TypeAtom) atom).atomicType(); + } + + public static SemType diff(SemType t1, SemType t2) { + int all1, all2, some1, some2; + if (t1 instanceof BasicTypeBitSet b1) { + if (t2 instanceof BasicTypeBitSet b2) { + return BasicTypeBitSet.from(b1.bitset & ~b2.bitset); + } else { + if (b1.bitset == 0) { + return t1; + } + ComplexSemType c2 = (ComplexSemType) t2; + all2 = c2.all(); + some2 = c2.some(); + } + all1 = b1.bitset; + some1 = 0; + } else { + ComplexSemType c1 = (ComplexSemType) t1; + all1 = c1.all(); + some1 = c1.some(); + if (t2 instanceof BasicTypeBitSet b2) { + if (b2.bitset == BasicTypeCode.VT_MASK) { + return BasicTypeBitSet.from(0); + } + all2 = b2.bitset; + some2 = 0; + } else { + ComplexSemType c2 = (ComplexSemType) t2; + all2 = c2.all(); + some2 = c2.some(); + } + } + BasicTypeBitSet all = BasicTypeBitSet.from(all1 & ~(all2 | some2)); + + int someBitset = (all1 | some1) & ~all2; + someBitset = someBitset & ~all.bitset; + BasicTypeBitSet some = BasicTypeBitSet.from(someBitset); + + if (some.bitset == 0) { + return PredefinedType.basicTypeUnion(all.bitset); + } + List subtypes = new ArrayList<>(); + + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + BasicTypeCode code = pair.basicTypeCode; + SubtypeData data1 = pair.subtypeData1; + SubtypeData data2 = pair.subtypeData2; + SubtypeData data; + if (data1 == null) { + data = OpsTable.OPS[code.code].complement(data2); + } else if (data2 == null) { + data = data1; + } else { + data = OpsTable.OPS[code.code].diff(data1, data2); + } + if (!(data instanceof AllOrNothingSubtype allOrNothingSubtype)) { + subtypes.add(BasicSubtype.from(code, (ProperSubtypeData) data)); + } else if (allOrNothingSubtype.isAllSubtype()) { + int c = code.code; + all = BasicTypeBitSet.from(all.bitset | (1 << c)); + } + // No need to consider `data == false` case. The `some` variable above is not used to create the SemType + } + if (subtypes.isEmpty()) { + return all; + } + return ComplexSemType.createComplexSemType(all.bitset, subtypes); + } + + public static List unpackComplexSemType(ComplexSemType t) { + int some = t.some(); + List subtypeList = new ArrayList<>(); + for (ProperSubtypeData data : t.subtypeDataList()) { + BasicTypeCode code = BasicTypeCode.from(Integer.numberOfTrailingZeros(some)); + subtypeList.add(BasicSubtype.from(code, data)); + int c = code.code; + some ^= (1 << c); + } + return subtypeList; + } + + public static SubtypeData getComplexSubtypeData(ComplexSemType t, BasicTypeCode code) { + int c = code.code; + c = 1 << c; + if ((t.all() & c) != 0) { + return AllOrNothingSubtype.createAll(); + } + if ((t.some() & c) == 0) { + return AllOrNothingSubtype.createNothing(); + } + int loBits = t.some() & (c - 1); + return t.subtypeDataList()[loBits == 0 ? 0 : Integer.bitCount(loBits)]; + } + + public static SemType union(SemType t1, SemType t2) { + assert t1 != null && t2 != null; + int all1, all2, some1, some2; + + if (t1 instanceof BasicTypeBitSet b1) { + if (t2 instanceof BasicTypeBitSet b2) { + return BasicTypeBitSet.from(b1.bitset | b2.bitset); + } else { + ComplexSemType complexT2 = (ComplexSemType) t2; + all2 = complexT2.all(); + some2 = complexT2.some(); + } + all1 = b1.bitset; + some1 = 0; + } else { + ComplexSemType complexT1 = (ComplexSemType) t1; + all1 = complexT1.all(); + some1 = complexT1.some(); + if (t2 instanceof BasicTypeBitSet b2) { + all2 = b2.bitset; + some2 = 0; + } else { + ComplexSemType complexT2 = (ComplexSemType) t2; + all2 = complexT2.all(); + some2 = complexT2.some(); + } + } + + BasicTypeBitSet all = BasicTypeBitSet.from(all1 | all2); + BasicTypeBitSet some = BasicTypeBitSet.from((some1 | some2) & ~all.bitset); + if (some.bitset == 0) { + return PredefinedType.basicTypeUnion(all.bitset); + } + + List subtypes = new ArrayList<>(); + + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + BasicTypeCode code = pair.basicTypeCode; + SubtypeData data1 = pair.subtypeData1; + SubtypeData data2 = pair.subtypeData2; + + SubtypeData data; + if (data1 == null) { + data = data2; // // [from original impl] if they are both null, something's gone wrong + } else if (data2 == null) { + data = data1; + } else { + data = OpsTable.OPS[code.code].union(data1, data2); + } + + if (data instanceof AllOrNothingSubtype allOrNothingSubtype && allOrNothingSubtype.isAllSubtype()) { + int c = code.code; + all = BasicTypeBitSet.from(all.bitset | 1 << c); + } else { + // data cannot be false since data1 and data2 are not both false + subtypes.add(BasicSubtype.from(code, (ProperSubtypeData) data)); + } + } + + if (subtypes.isEmpty()) { + return all; + } + return ComplexSemType.createComplexSemType(all.bitset, subtypes); + } + + public static SemType intersect(SemType t1, SemType t2) { + int all1, all2, some1, some2; + + if (t1 instanceof BasicTypeBitSet b1) { + if (t2 instanceof BasicTypeBitSet b2) { + return BasicTypeBitSet.from(b1.bitset & b2.bitset); + } else { + if (b1.bitset == 0) { + return t1; + } + if (b1.bitset == VT_MASK) { + return t2; + } + ComplexSemType complexT2 = (ComplexSemType) t2; + all2 = complexT2.all(); + some2 = complexT2.some(); + } + all1 = b1.bitset; + some1 = 0; + } else { + ComplexSemType complexT1 = (ComplexSemType) t1; + all1 = complexT1.all(); + some1 = complexT1.some(); + if (t2 instanceof BasicTypeBitSet b2) { + if (b2.bitset == 0) { + return t2; + } + if (b2.bitset == VT_MASK) { + return t1; + } + all2 = b2.bitset; + some2 = 0; + } else { + ComplexSemType complexT2 = (ComplexSemType) t2; + all2 = complexT2.all(); + some2 = complexT2.some(); + } + } + + BasicTypeBitSet all = BasicTypeBitSet.from(all1 & all2); + BasicTypeBitSet some = BasicTypeBitSet.from((some1 | all1) & (some2 | all2)); + some = BasicTypeBitSet.from(some.bitset & ~all.bitset); + if (some.bitset == 0) { + return PredefinedType.basicTypeUnion(all.bitset); + } + + List subtypes = new ArrayList<>(); + + for (SubtypePair pair : new SubtypePairs(t1, t2, some)) { + BasicTypeCode code = pair.basicTypeCode; + SubtypeData data1 = pair.subtypeData1; + SubtypeData data2 = pair.subtypeData2; + + SubtypeData data; + if (data1 == null) { + data = data2; + } else if (data2 == null) { + data = data1; + } else { + data = OpsTable.OPS[code.code].intersect(data1, data2); + } + if (!(data instanceof AllOrNothingSubtype allOrNothingSubtype) || allOrNothingSubtype.isAllSubtype()) { + subtypes.add(BasicSubtype.from(code, (ProperSubtypeData) data)); + } + } + if (subtypes.isEmpty()) { + return all; + } + return ComplexSemType.createComplexSemType(all.bitset, subtypes); + } + + public static CellSemType intersectMemberSemTypes(Env env, CellSemType t1, CellSemType t2) { + CellAtomicType c1 = cellAtomicType(t1); + CellAtomicType c2 = cellAtomicType(t2); + assert c1 != null && c2 != null; + CellAtomicType atomicType = intersectCellAtomicType(c1, c2); + return cellContaining(env, atomicType.ty(), UNDEF.equals(atomicType.ty()) ? CELL_MUT_NONE : atomicType.mut()); + } + + public static SemType complement(SemType t) { + return diff(VAL, t); + } + + public static boolean isNever(SemType t) { + return (t instanceof BasicTypeBitSet b) && b.bitset == 0; + } + + public static boolean isEmpty(Context cx, SemType t) { + assert t != null && cx != null; + if (t instanceof BasicTypeBitSet b) { + return b.bitset == 0; + } else { + ComplexSemType ct = (ComplexSemType) t; + if (ct.all() != 0) { + // includes all of, one or more basic types + return false; + } + for (var st : unpackComplexSemType(ct)) { + if (!OpsTable.OPS[st.basicTypeCode.code].isEmpty(cx, st.subtypeData)) { + return false; + } + } + return true; + } + } + + public static boolean isSubtype(Context cx, SemType t1, SemType t2) { + return isEmpty(cx, diff(t1, t2)); + } + + public static boolean isSubtypeSimple(SemType t1, BasicTypeBitSet t2) { + int bits; + if (t1 instanceof BasicTypeBitSet b1) { + bits = b1.bitset; + } else { + ComplexSemType complexT1 = (ComplexSemType) t1; + bits = complexT1.all() | complexT1.some(); + } + return (bits & ~t2.bitset) == 0; + } + + public static boolean isSameType(Context context, SemType t1, SemType t2) { + return isSubtype(context, t1, t2) && isSubtype(context, t2, t1); + } + + public static BasicTypeBitSet widenToBasicTypes(SemType t) { + if (t instanceof BasicTypeBitSet b) { + return b; + } else { + ComplexSemType complexSemType = (ComplexSemType) t; + return BasicTypeBitSet.from(complexSemType.all() | complexSemType.some()); + } + } + + // If t is a non-empty subtype of a built-in unsigned int subtype (Unsigned8/16/32), + // then return the smallest such subtype. Otherwise, return t. + public static SemType wideUnsigned(SemType t) { + if (t instanceof BasicTypeBitSet) { + return t; + } else { + if (!isSubtypeSimple(t, PredefinedType.INT)) { + return t; + } + SubtypeData data = IntSubtype.intSubtypeWidenUnsigned(subtypeData(t, BT_INT)); + if (data instanceof AllOrNothingSubtype) { + return PredefinedType.INT; + } else { + return PredefinedType.basicSubtype(BT_INT, (ProperSubtypeData) data); + } + } + } + + public static SubtypeData booleanSubtype(SemType t) { + return subtypeData(t, BT_BOOLEAN); + } + + // Describes the subtype of int included in the type: true/false mean all or none of string + public static SubtypeData intSubtype(SemType t) { + return subtypeData(t, BT_INT); + } + + public static SubtypeData floatSubtype(SemType t) { + return subtypeData(t, BT_FLOAT); + } + + public static SubtypeData decimalSubtype(SemType t) { + return subtypeData(t, BT_DECIMAL); + } + + // Describes the subtype of string included in the type: true/false mean all or none of string + public static SubtypeData stringSubtype(SemType t) { + return subtypeData(t, BT_STRING); + } + + // This computes the spec operation called "member type of K in T", + // for the case when T is a subtype of list, and K is either `int` or a singleton int. + // This is what Castagna calls projection. + // We will extend this to allow `key` to be a SemType, which will turn into an IntSubtype. + // If `t` is not a list, NEVER is returned + public static SemType listMemberTypeInnerVal(Context cx, SemType t, SemType k) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & LIST.bitset) != 0 ? VAL : NEVER; + } else { + SubtypeData keyData = intSubtype(k); + if (isNothingSubtype(keyData)) { + return NEVER; + } + return bddListMemberTypeInnerVal(cx, (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_LIST), keyData, + VAL); + } + } + + static final ListMemberTypes LIST_MEMBER_TYPES_ALL = ListMemberTypes.from( + List.of(Range.from(0, MAX_VALUE)), + List.of(VAL) + ); + + static final ListMemberTypes LIST_MEMBER_TYPES_NONE = ListMemberTypes.from(List.of(), List.of()); + + public static ListMemberTypes listAllMemberTypesInner(Context cx, SemType t) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & LIST.bitset) != 0 ? LIST_MEMBER_TYPES_ALL : LIST_MEMBER_TYPES_NONE; + } + + ComplexSemType ct = (ComplexSemType) t; + List ranges = new ArrayList<>(); + List types = new ArrayList<>(); + + + Range[] allRanges = bddListAllRanges(cx, (Bdd) getComplexSubtypeData(ct, BT_LIST), new Range[]{}); + for (Range r : allRanges) { + SemType m = listMemberTypeInnerVal(cx, t, IntSubtype.intConst(r.min)); + if (!NEVER.equals(m)) { + ranges.add(r); + types.add(m); + } + } + return ListMemberTypes.from(ranges, types); + } + + static Range[] bddListAllRanges(Context cx, Bdd b, Range[] accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : new Range[0]; + } else { + BddNode bddNode = (BddNode) b; + ListMemberTypes listMemberTypes = listAtomicTypeAllMemberTypesInnerVal(cx.listAtomType(bddNode.atom())); + return distinctRanges(bddListAllRanges(cx, bddNode.left(), + distinctRanges(listMemberTypes.ranges().toArray(Range[]::new), accum)), + distinctRanges(bddListAllRanges(cx, bddNode.middle(), accum), + bddListAllRanges(cx, bddNode.right(), accum))); + } + } + + static Range[] distinctRanges(Range[] range1, Range[] range2) { + CombinedRange[] combined = combineRanges(range1, range2); + Range[] range = new Range[combined.length]; + for (int i = 0; i < combined.length; i++) { + range[i] = combined[i].range(); + } + return range; + } + + // If [r, i1, i2] is included in the result, then + // at least one of i1 and i2 are not () + // if i1 is not (), then r is completely included in ranges1[i1] + // if i2 is not (), then r is completely included in ranges2[i2] + // The ranges in the result are ordered and non-overlapping. + public static CombinedRange[] combineRanges(Range[] ranges1, Range[] ranges2) { + List combined = new ArrayList<>(); + int i1 = 0; + int i2 = 0; + int len1 = ranges1.length; + int len2 = ranges2.length; + long cur = MIN_VALUE; + // This iterates over the boundaries between ranges + while (true) { + while (i1 < len1 && cur > ranges1[i1].max) { + i1 += 1; + } + while (i2 < len2 && cur > ranges2[i2].max) { + i2 += 1; + } + + Long next = null; + if (i1 < len1) { + next = nextBoundary(cur, ranges1[i1], next); + } + if (i2 < len2) { + next = nextBoundary(cur, ranges2[i2], next); + } + long max = next == null ? MAX_VALUE : next - 1; + Long in1 = null; + if (i1 < len1) { + Range r = ranges1[i1]; + if (cur >= r.min && max <= r.max) { + in1 = (long) i1; + } + } + Long in2 = null; + if (i2 < len2) { + Range r = ranges2[i2]; + if (cur >= r.min && max <= r.max) { + in2 = (long) i2; + } + } + if (in1 != null || in2 != null) { + combined.add(CombinedRange.from(Range.from(cur, max), in1, in2)); + } + if (next == null) { + break; + } + cur = next; + } + return combined.toArray(CombinedRange[]::new); + } + + // Helper function for combineRanges + // Return smallest range boundary that is > cur and <= next + // null represents int:MAX_VALUE + 1 + static Long nextBoundary(long cur, Range r, Long next) { + if ((r.min > cur) && (next == null || r.min < next)) { + return r.min; + } + if (r.max != MAX_VALUE) { + long i = r.max + 1; + if (i > cur && (next == null || i < next)) { + return i; + } + } + return next; + } + + public static ListMemberTypes listAtomicTypeAllMemberTypesInnerVal(ListAtomicType atomicType) { + List ranges = new ArrayList<>(); + List types = new ArrayList<>(); + + List cellInitial = atomicType.members().initial(); + int initialLength = cellInitial.size(); + + List initial = new ArrayList<>(initialLength); + for (CellSemType c : cellInitial) { + initial.add(cellInnerVal(c)); + } + + int fixedLength = atomicType.members().fixedLength(); + if (initialLength != 0) { + types.addAll(initial); + for (int i = 0; i < initialLength; i++) { + ranges.add(Range.from(i, i)); + } + if (initialLength < fixedLength) { + ranges.set(initialLength - 1, Range.from(initialLength - 1, fixedLength - 1)); + } + } + + SemType rest = cellInnerVal(atomicType.rest()); + if (!Core.isNever(rest)) { + types.add(rest); + ranges.add(Range.from(fixedLength, MAX_VALUE)); + } + + return ListMemberTypes.from(ranges, types); + } + + public static MappingAtomicType mappingAtomicType(Context cx, SemType t) { + MappingAtomicType mappingAtomicInner = MAPPING_ATOMIC_INNER; + if (t instanceof BasicTypeBitSet b) { + return b.bitset == MAPPING.bitset ? mappingAtomicInner : null; + } else { + Env env = cx.env; + if (!isSubtypeSimple(t, MAPPING)) { + return null; + } + return bddMappingAtomicType(env, + (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_MAPPING), + mappingAtomicInner); + } + } + + private static MappingAtomicType bddMappingAtomicType(Env env, Bdd bdd, MappingAtomicType top) { + if (bdd instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + return top; + } + return null; + } + BddNode bddNode = (BddNode) bdd; + if (bddNode instanceof BddNodeSimple bddNodeSimple) { + return env.mappingAtomType(bddNodeSimple.atom()); + } + return null; + } + + public static SemType mappingMemberTypeInnerVal(Context cx, SemType t, SemType k) { + return diff(mappingMemberTypeInner(cx, t, k), UNDEF); + } + + // This computes the spec operation called "member type of K in T", + // for when T is a subtype of mapping, and K is either `string` or a singleton string. + // This is what Castagna calls projection. + public static SemType mappingMemberTypeInner(Context cx, SemType t, SemType k) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & MAPPING.bitset) != 0 ? VAL : UNDEF; + } else { + SubtypeData keyData = stringSubtype(k); + if (isNothingSubtype(keyData)) { + return UNDEF; + } + return bddMappingMemberTypeInner(cx, (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_MAPPING), keyData, + INNER); + } + } + + public static ListAtomicType listAtomicType(Context cx, SemType t) { + ListAtomicType listAtomicInner = LIST_ATOMIC_INNER; + if (t instanceof BasicTypeBitSet b) { + return b.bitset == LIST.bitset ? listAtomicInner : null; + } else { + Env env = cx.env; + if (!isSubtypeSimple(t, LIST)) { + return null; + } + return bddListAtomicType(env, + (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_LIST), + listAtomicInner); + } + } + + private static ListAtomicType bddListAtomicType(Env env, Bdd bdd, ListAtomicType top) { + if (bdd instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + return top; + } + return null; + } + BddNode bddNode = (BddNode) bdd; + if (bddNode instanceof BddNodeSimple bddNodeSimple) { + return env.listAtomType(bddNodeSimple.atom()); + } + return null; + } + + public static SemType cellInnerVal(CellSemType t) { + return diff(cellInner(t), UNDEF); + } + + public static SemType cellInner(CellSemType t) { + CellAtomicType cat = cellAtomicType(t); + assert cat != null; + return cat.ty(); + } + + public static CellSemType cellContainingInnerVal(Env env, CellSemType t) { + CellAtomicType cat = cellAtomicType(t); + assert cat != null; + return cellContaining(env, diff(cat.ty(), UNDEF), cat.mut()); + } + + public static CellAtomicType cellAtomicType(SemType t) { + if (t instanceof BasicTypeBitSet) { + return PredefinedType.CELL.equals(t) ? CELL_ATOMIC_VAL : null; + } else { + if (!isSubtypeSimple(t, PredefinedType.CELL)) { + return null; + } + return bddCellAtomicType((Bdd) getComplexSubtypeData((ComplexSemType) t, BT_CELL), CELL_ATOMIC_VAL); + } + } + + static CellAtomicType bddCellAtomicType(Bdd bdd, CellAtomicType top) { + if (bdd instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + return top; + } + return null; + } + BddNode bddNode = (BddNode) bdd; + if (bddNode.left().equals(BddAllOrNothing.bddAll()) && + bddNode.middle().equals(BddAllOrNothing.bddNothing()) && + bddNode.right().equals(BddAllOrNothing.bddNothing())) { + return cellAtomType(bddNode.atom()); + } + return null; + } + + public static Optional singleShape(SemType t) { + if (PredefinedType.NIL.equals(t)) { + return Optional.of(Value.from(null)); + } else if (t instanceof BasicTypeBitSet) { + return Optional.empty(); + } else if (isSubtypeSimple(t, PredefinedType.INT)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_INT); + Optional value = IntSubtype.intSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(Value.from(value.get())); + } else if (isSubtypeSimple(t, PredefinedType.FLOAT)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_FLOAT); + Optional value = FloatSubtype.floatSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(Value.from(value.get())); + } else if (isSubtypeSimple(t, PredefinedType.STRING)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_STRING); + Optional value = StringSubtype.stringSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(Value.from(value.get())); + } else if (isSubtypeSimple(t, PredefinedType.BOOLEAN)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_BOOLEAN); + Optional value = BooleanSubtype.booleanSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(Value.from(value.get())); + } else if (isSubtypeSimple(t, PredefinedType.DECIMAL)) { + SubtypeData sd = getComplexSubtypeData((ComplexSemType) t, BT_DECIMAL); + Optional value = DecimalSubtype.decimalSubtypeSingleValue(sd); + return value.isEmpty() ? Optional.empty() : Optional.of(Value.from(value.get().toString())); + } + return Optional.empty(); + } + + public static SemType singleton(Object v) { + if (v == null) { + return PredefinedType.NIL; + } + + if (v instanceof Long lng) { + return IntSubtype.intConst(lng); + } else if (v instanceof Double d) { + return FloatSubtype.floatConst(d); + } else if (v instanceof String s) { + return StringSubtype.stringConst(s); + } else if (v instanceof Boolean b) { + return BooleanSubtype.booleanConst(b); + } else { + throw new IllegalStateException("Unsupported type: " + v.getClass().getName()); + } + } + + public static boolean containsConst(SemType t, Object v) { + if (v == null) { + return containsNil(t); + } else if (v instanceof Long lng) { + return containsConstInt(t, lng); + } else if (v instanceof Double d) { + return containsConstFloat(t, d); + } else if (v instanceof String s) { + return containsConstString(t, s); + } else if (v instanceof Boolean b) { + return containsConstBoolean(t, b); + } else { + return containsConstDecimal(t, (BigDecimal) v); + } + } + + public static boolean containsNil(SemType t) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_NIL.code)) != 0; + } else { + // todo: Need to verify this behavior + AllOrNothingSubtype complexSubtypeData = + (AllOrNothingSubtype) getComplexSubtypeData((ComplexSemType) t, BT_NIL); + return complexSubtypeData.isAllSubtype(); + } + } + + + public static boolean containsConstString(SemType t, String s) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_STRING.code)) != 0; + } else { + return StringSubtype.stringSubtypeContains( + getComplexSubtypeData((ComplexSemType) t, BT_STRING), s); + } + } + + public static boolean containsConstInt(SemType t, long n) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_INT.code)) != 0; + } else { + return IntSubtype.intSubtypeContains( + getComplexSubtypeData((ComplexSemType) t, BT_INT), n); + } + } + + public static boolean containsConstFloat(SemType t, double n) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_FLOAT.code)) != 0; + } else { + return FloatSubtype.floatSubtypeContains( + getComplexSubtypeData((ComplexSemType) t, BT_FLOAT), EnumerableFloat.from(n)); + } + } + + public static boolean containsConstDecimal(SemType t, BigDecimal n) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_DECIMAL.code)) != 0; + } else { + return DecimalSubtype.decimalSubtypeContains( + getComplexSubtypeData((ComplexSemType) t, BT_DECIMAL), EnumerableDecimal.from(n)); + } + } + + public static boolean containsConstBoolean(SemType t, boolean bool) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & (1 << BT_BOOLEAN.code)) != 0; + } else { + return BooleanSubtype.booleanSubtypeContains( + getComplexSubtypeData((ComplexSemType) t, BT_BOOLEAN), bool); + } + } + + public static Optional singleNumericType(SemType semType) { + SemType numType = intersect(semType, PredefinedType.NUMBER); + if (numType instanceof BasicTypeBitSet b) { + if (b.bitset == NEVER.bitset) { + return Optional.empty(); + } + } + if (isSubtypeSimple(numType, PredefinedType.INT)) { + return Optional.of(PredefinedType.INT); + } + if (isSubtypeSimple(numType, PredefinedType.FLOAT)) { + return Optional.of(PredefinedType.FLOAT); + } + if (isSubtypeSimple(numType, PredefinedType.DECIMAL)) { + return Optional.of(PredefinedType.DECIMAL); + } + return Optional.empty(); + } + + public static SubtypeData subtypeData(SemType s, BasicTypeCode code) { + if (s instanceof BasicTypeBitSet b) { + if ((b.bitset & (1 << code.code)) != 0) { + return AllOrNothingSubtype.createAll(); + } + return AllOrNothingSubtype.createNothing(); + } else { + return getComplexSubtypeData((ComplexSemType) s, code); + } + } + + public static Context typeCheckContext(Env env) { + return Context.from(env); + } + + public static SemType createJson(Context context) { + SemType memo = context.jsonMemo; + Env env = context.env; + + if (memo != null) { + return memo; + } + ListDefinition listDef = new ListDefinition(); + MappingDefinition mapDef = new MappingDefinition(); + SemType j = union(PredefinedType.SIMPLE_OR_STRING, union(listDef.getSemType(env), mapDef.getSemType(env))); + listDef.defineListTypeWrapped(env, j); + mapDef.defineMappingTypeWrapped(env, new ArrayList<>(), j); + context.jsonMemo = j; + return j; + } + + public static SemType createAnydata(Context context) { + SemType memo = context.anydataMemo; + Env env = context.env; + + if (memo != null) { + return memo; + } + ListDefinition listDef = new ListDefinition(); + MappingDefinition mapDef = new MappingDefinition(); + SemType tableTy = TableSubtype.tableContaining(env, mapDef.getSemType(env)); + SemType ad = union(union(SIMPLE_OR_STRING, union(XML, union(REGEXP, tableTy))), + union(listDef.getSemType(env), mapDef.getSemType(env))); + listDef.defineListTypeWrapped(env, ad); + mapDef.defineMappingTypeWrapped(env, new ArrayList<>(), ad); + context.anydataMemo = ad; + return ad; + } + + public static SemType createCloneable(Context context) { + SemType memo = context.cloneableMemo; + Env env = context.env; + + if (memo != null) { + return memo; + } + ListDefinition listDef = new ListDefinition(); + MappingDefinition mapDef = new MappingDefinition(); + SemType tableTy = TableSubtype.tableContaining(env, mapDef.getSemType(env)); + SemType ad = union(VAL_READONLY, union(XML, union(listDef.getSemType(env), union(tableTy, + mapDef.getSemType(env))))); + listDef.defineListTypeWrapped(env, ad); + mapDef.defineMappingTypeWrapped(env, new ArrayList<>(), ad); + context.cloneableMemo = ad; + return ad; + } + + public static SemType createIsolatedObject(Context context) { + SemType memo = context.isolatedObjectMemo; + if (memo != null) { + return memo; + } + + ObjectQualifiers quals = new ObjectQualifiers(true, false, ObjectQualifiers.NetworkQualifier.None); + SemType isolatedObj = new ObjectDefinition().define(context.env, quals, Collections.emptyList()); + context.isolatedObjectMemo = isolatedObj; + return isolatedObj; + } + + public static SemType createServiceObject(Context context) { + SemType memo = context.serviceObjectMemo; + if (memo != null) { + return memo; + } + + ObjectQualifiers quals = new ObjectQualifiers(false, false, ObjectQualifiers.NetworkQualifier.Service); + SemType serviceObj = new ObjectDefinition().define(context.env, quals, Collections.emptyList()); + context.serviceObjectMemo = serviceObj; + return serviceObj; + } + + public static SemType createBasicSemType(BasicTypeCode typeCode, SubtypeData subtypeData) { + if (subtypeData instanceof AllOrNothingSubtype) { + if (Common.isAllSubtype(subtypeData)) { + return BasicTypeBitSet.from(1 << typeCode.code); + } else { + return BasicTypeBitSet.from(0); + } + } else { + return ComplexSemType.createComplexSemType(0, + BasicSubtype.from(typeCode, (ProperSubtypeData) subtypeData)); + } + } + + // ------------------------- Newly Introduced APIs (Does not exist in nBallerina) -------------------------------- + + // Consider map|map|...|map. This API will return all MappingAtomicTypes in the union. + public static Optional> mappingAtomicTypesInUnion(Context cx, SemType t) { + ArrayList matList = new ArrayList<>(); + MappingAtomicType mappingAtomicInner = MAPPING_ATOMIC_INNER; + if (t instanceof BasicTypeBitSet b) { + if (b.bitset == MAPPING.bitset) { + matList.add(mappingAtomicInner); + return Optional.of(matList); + } + return Optional.empty(); + } else { + Env env = cx.env; + if (!isSubtypeSimple(t, MAPPING)) { + return Optional.empty(); + } + return collectBddMappingAtomicTypesInUnion(env, + (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_MAPPING), + mappingAtomicInner, matList) ? Optional.of(matList) : Optional.empty(); + } + } + + private static boolean collectBddMappingAtomicTypesInUnion(Env env, Bdd bdd, MappingAtomicType top, + List matList) { + if (bdd instanceof BddAllOrNothing allOrNothing) { + if (allOrNothing.isAll()) { + matList.add(top); + return true; + } + return false; + } + BddNode bddNode = (BddNode) bdd; + if (bddNode instanceof BddNodeSimple bddNodeSimple) { + matList.add(env.mappingAtomType(bddNodeSimple.atom())); + return true; + } + + BddNodeImpl bddNodeImpl = (BddNodeImpl) bddNode; + if (bddNodeImpl.left() instanceof BddAllOrNothing leftNode && leftNode.isAll() && + bddNodeImpl.right() instanceof BddAllOrNothing rightNode && rightNode.isNothing()) { + matList.add(env.mappingAtomType(bddNodeImpl.atom())); + return collectBddMappingAtomicTypesInUnion(env, bddNodeImpl.middle(), top, matList); + } + + return false; + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/Definition.java b/semtypes/src/main/java/io/ballerina/types/Definition.java similarity index 70% rename from semtypes/src/main/java/io/ballerina/semtype/Definition.java rename to semtypes/src/main/java/io/ballerina/types/Definition.java index da0d79a9bf6a..6b0a359083fa 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/Definition.java +++ b/semtypes/src/main/java/io/ballerina/types/Definition.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,16 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Super type for type-descs. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface Definition { SemType getSemType(Env env); diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableCharString.java b/semtypes/src/main/java/io/ballerina/types/EnumerableCharString.java new file mode 100644 index 000000000000..d017d133c65c --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableCharString.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Enumerable type wrapper for string. + * + * @since 2201.8.0 + */ +public class EnumerableCharString implements EnumerableType { + // String since Java char can't hold some Unicode characters + public final String value; + + private EnumerableCharString(String value) { + this.value = value; + } + + public static EnumerableCharString from(String v) { + return new EnumerableCharString(v); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EnumerableCharString e)) { + return false; + } + return (e.value.equals(this.value)); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableDecimal.java b/semtypes/src/main/java/io/ballerina/types/EnumerableDecimal.java new file mode 100644 index 000000000000..09fc64e5fdf9 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableDecimal.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.math.BigDecimal; + +/** + * Enumerable type wrapper for decimal. + * + * @since 2201.8.0 + */ +public class EnumerableDecimal implements EnumerableType { + public final BigDecimal value; + + private EnumerableDecimal(BigDecimal value) { + this.value = value; + } + + public static EnumerableDecimal from(BigDecimal d) { + return new EnumerableDecimal(d); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EnumerableDecimal e)) { + return false; + } + return (e.value.compareTo(this.value) == 0); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableFloat.java b/semtypes/src/main/java/io/ballerina/types/EnumerableFloat.java new file mode 100644 index 000000000000..09531897daef --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableFloat.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Enumerable type wrapper for float. + * + * @since 2201.8.0 + */ +public class EnumerableFloat implements EnumerableType { + public final double value; + + private EnumerableFloat(double value) { + this.value = value; + } + + public static EnumerableFloat from(double d) { + return new EnumerableFloat(d); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EnumerableFloat e)) { + return false; + } + + Double v1 = e.value; + Double v2 = this.value; + return (v1.compareTo(v2) == 0); + } + + @Override + public int hashCode() { + return Double.hashCode(value); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableString.java b/semtypes/src/main/java/io/ballerina/types/EnumerableString.java new file mode 100644 index 000000000000..55a2c8958d4f --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableString.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Enumerable type wrapper for string. + * + * @since 2201.8.0 + */ +public class EnumerableString implements EnumerableType { + public final String value; + + private EnumerableString(String value) { + this.value = value; + } + + public static EnumerableString from(String v) { + return new EnumerableString(v); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof EnumerableString e)) { + return false; + } + return (e.value.equals(this.value)); + } + + @Override + public int hashCode() { + return value.hashCode(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableSubtype.java b/semtypes/src/main/java/io/ballerina/types/EnumerableSubtype.java new file mode 100644 index 000000000000..68c5134ed7eb --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableSubtype.java @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Objects; + +/** + * EnumerableSubtype with enumerable subtype ops. + * + * @since 2201.8.0 + */ +public abstract class EnumerableSubtype { + public static final int LT = -1; + public static final int EQ = 0; + public static final int GT = 1; + + public abstract boolean allowed(); + public abstract EnumerableType[] values(); + + public static boolean enumerableSubtypeUnion(EnumerableSubtype t1, EnumerableSubtype t2, + List result) { + boolean b1 = t1.allowed(); + boolean b2 = t2.allowed(); + boolean allowed; + if (b1 && b2) { + enumerableListUnion(t1.values(), t2.values(), result); + allowed = true; + } else if (!b1 && !b2) { + enumerableListIntersect(t1.values(), t2.values(), result); + allowed = false; + } else if (b1 && !b2) { + enumerableListDiff(t2.values(), t1.values(), result); + allowed = false; + } else { + // !b1 && b2 + enumerableListDiff(t1.values(), t2.values(), result); + allowed = false; + } + return allowed; + } + + public static boolean enumerableSubtypeIntersect(EnumerableSubtype t1, EnumerableSubtype t2, + List result) { + boolean b1 = t1.allowed(); + boolean b2 = t2.allowed(); + boolean allowed; + if (b1 && b2) { + enumerableListIntersect(t1.values(), t2.values(), result); + allowed = true; + } else if (!b1 && !b2) { + enumerableListUnion(t1.values(), t2.values(), result); + allowed = false; + } else if (b1 && !b2) { + enumerableListDiff(t1.values(), t2.values(), result); + allowed = true; + } else { + // !b1 && b2 + enumerableListDiff(t2.values(), t1.values(), result); + allowed = true; + } + return allowed; + } + + public static void enumerableListUnion(EnumerableType[] v1, EnumerableType[] v2, + List resulte) { + List result = (List) resulte; + int i1 = 0; + int i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + + while (true) { + if (i1 >= len1) { + if (i2 >= len2) { + break; + } + result.add(v2[i2]); + i2 += 1; + } else if (i2 >= len2) { + result.add(v1[i1]); + i1 += 1; + } else { + EnumerableType s1 = v1[i1]; + EnumerableType s2 = v2[i2]; + switch (compareEnumerable(s1, s2)) { + case EQ: + result.add(s1); + i1 += 1; + i2 += 1; + break; + case LT: + result.add(s1); + i1 += 1; + break; + case GT: + result.add(s2); + i2 += 1; + break; + } + } + } + } + + public static void enumerableListIntersect(EnumerableType[] v1, EnumerableType[] v2, + List resulte) { + List result = (List) resulte; + int i1 = 0; + int i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + + while (true) { + if (i1 >= len1 || i2 >= len2) { + break; + } else { + EnumerableType s1 = v1[i1]; + EnumerableType s2 = v2[i2]; + switch (compareEnumerable(s1, s2)) { + case EQ: + result.add(s1); + i1 += 1; + i2 += 1; + break; + case LT: + i1 += 1; + break; + case GT: + i2 += 1; + break; + } + } + } + } + + public static void enumerableListDiff(EnumerableType[] v1, EnumerableType[] v2, + List resulte) { + List result = (List) resulte; + int i1 = 0; + int i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + + while (true) { + if (i1 >= len1) { + break; + } + if (i2 >= len2) { + result.add(v1[i1]); + i1 += 1; + } else { + EnumerableType s1 = v1[i1]; + EnumerableType s2 = v2[i2]; + switch (compareEnumerable(s1, s2)) { + case EQ: + i1 += 1; + i2 += 1; + break; + case LT: + result.add(s1); + i1 += 1; + break; + case GT: + i2 += 1; + break; + } + } + } + } + + public static int compareEnumerable(EnumerableType v1, EnumerableType v2) { + if (v1 instanceof EnumerableString) { + String s2 = ((EnumerableString) v2).value; + String s1 = ((EnumerableString) v1).value; + return Objects.equals(s1, s2) ? EQ : (Common.codePointCompare(s1, s2) ? LT : GT); + } else if (v1 instanceof EnumerableCharString) { + String s2 = ((EnumerableCharString) v2).value + ""; + String s1 = ((EnumerableCharString) v1).value + ""; + return Objects.equals(s1, s2) ? EQ : (Common.codePointCompare(s1, s2) ? LT : GT); + } else if (v1 instanceof EnumerableDecimal) { + BigDecimal d2 = ((EnumerableDecimal) v2).value; + BigDecimal d1 = ((EnumerableDecimal) v1).value; + return d1.compareTo(d2); + } else { + double f1 = ((EnumerableFloat) v1).value; + double f2 = ((EnumerableFloat) v2).value; + if (bFloatEq(f1, f2)) { + return EQ; + } else if (Double.isNaN(f1)) { + return LT; + } else if (Double.isNaN(f2)) { + return GT; + } else if (f1 < f2) { + return LT; + } + return GT; + } + } + + private static boolean bFloatEq(double f1, double f2) { + if (Double.isNaN(f1)) { + return Double.isNaN(f2); + } + return f1 == f2; + } +} + diff --git a/semtypes/src/main/java/io/ballerina/types/EnumerableType.java b/semtypes/src/main/java/io/ballerina/types/EnumerableType.java new file mode 100644 index 000000000000..04c46676fa85 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/EnumerableType.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Interface to indicate Enumerable types. + * + * @since 2201.8.0 + */ +public interface EnumerableType { +} diff --git a/semtypes/src/main/java/io/ballerina/types/Env.java b/semtypes/src/main/java/io/ballerina/types/Env.java new file mode 100644 index 000000000000..f9b512b72c13 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Env.java @@ -0,0 +1,304 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.lang.ref.Reference; +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Env node. + * + * @since 2201.8.0 + */ +public class Env { + + private static final int COMPACT_INDEX = 3; + final List recListAtoms; + final List recMappingAtoms; + final List recFunctionAtoms; + private final AtomicInteger distinctAtomCount; + private final Map> atomTable; + + private final LinkedHashMap types; + + public Env() { + this.atomTable = new WeakHashMap<>(); + this.recListAtoms = new ArrayList<>(); + this.recMappingAtoms = new ArrayList<>(); + this.recFunctionAtoms = new ArrayList<>(); + types = new LinkedHashMap<>(); + distinctAtomCount = new AtomicInteger(0); + + PredefinedTypeEnv.getInstance().initializeEnv(this); + } + + public int recListAtomCount() { + return this.recListAtoms.size(); + } + + public int recMappingAtomCount() { + return this.recMappingAtoms.size(); + } + + public int recFunctionAtomCount() { + return this.recFunctionAtoms.size(); + } + + public int distinctAtomCount() { + return this.distinctAtomCount.get(); + } + + public int distinctAtomCountGetAndIncrement() { + return this.distinctAtomCount.getAndIncrement(); + } + + public RecAtom recFunctionAtom() { + synchronized (this.recFunctionAtoms) { + int result = this.recFunctionAtoms.size(); + // represents adding () in nballerina + this.recFunctionAtoms.add(null); + return RecAtom.createRecAtom(result); + } + } + + public void setRecFunctionAtomType(RecAtom ra, FunctionAtomicType atomicType) { + synchronized (this.recFunctionAtoms) { + ra.setKind(Atom.Kind.FUNCTION_ATOM); + this.recFunctionAtoms.set(ra.index, atomicType); + } + } + + public FunctionAtomicType getRecFunctionAtomType(RecAtom ra) { + synchronized (this.recFunctionAtoms) { + return this.recFunctionAtoms.get(ra.index); + } + } + + public TypeAtom listAtom(ListAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + public TypeAtom mappingAtom(MappingAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + public TypeAtom functionAtom(FunctionAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + public TypeAtom cellAtom(CellAtomicType atomicType) { + return this.typeAtom(atomicType); + } + + private TypeAtom typeAtom(AtomicType atomicType) { + synchronized (this.atomTable) { + Reference ref = this.atomTable.get(atomicType); + if (ref != null) { + TypeAtom ta = ref.get(); + if (ta != null) { + return ta; + } + } + TypeAtom result = TypeAtom.createTypeAtom(this.atomTable.size(), atomicType); + this.atomTable.put(result.atomicType(), new WeakReference<>(result)); + return result; + } + } + + public void deserializeTypeAtom(TypeAtom typeAtom) { + synchronized (this.atomTable) { + this.atomTable.put(typeAtom.atomicType(), new WeakReference<>(typeAtom)); + } + } + + public void insertRecAtomAtIndex(int index, AtomicType atomicType) { + if (atomicType instanceof MappingAtomicType mappingAtomicType) { + insertAtomAtIndexInner(index, this.recMappingAtoms, mappingAtomicType); + } else if (atomicType instanceof ListAtomicType listAtomicType) { + insertAtomAtIndexInner(index, this.recListAtoms, listAtomicType); + } else if (atomicType instanceof FunctionAtomicType functionAtomicType) { + insertAtomAtIndexInner(index, this.recFunctionAtoms, functionAtomicType); + } else { + throw new UnsupportedOperationException("Unknown atomic type " + atomicType); + } + } + + private void insertAtomAtIndexInner(int index, List atoms, E atomicType) { + // atoms are always private final fields therefore synchronizing on them should be safe. + synchronized (atoms) { + if (atoms.size() > index && atoms.get(index) != null) { + return; + } + while (atoms.size() < index + 1) { + atoms.add(null); + } + atoms.set(index, atomicType); + } + } + + public ListAtomicType listAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return getRecListAtomType(recAtom); + } else { + return (ListAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public FunctionAtomicType functionAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return getRecFunctionAtomType(recAtom); + } else { + return (FunctionAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public MappingAtomicType mappingAtomType(Atom atom) { + if (atom instanceof RecAtom recAtom) { + return getRecMappingAtomType(recAtom); + } else { + return (MappingAtomicType) ((TypeAtom) atom).atomicType(); + } + } + + public RecAtom recListAtom() { + synchronized (this.recListAtoms) { + int result = this.recListAtoms.size(); + this.recListAtoms.add(null); + return RecAtom.createRecAtom(result); + } + } + + public RecAtom recMappingAtom() { + synchronized (this.recMappingAtoms) { + int result = this.recMappingAtoms.size(); + this.recMappingAtoms.add(null); + return RecAtom.createRecAtom(result); + } + } + + public void setRecListAtomType(RecAtom ra, ListAtomicType atomicType) { + synchronized (this.recListAtoms) { + ra.setKind(Atom.Kind.LIST_ATOM); + this.recListAtoms.set(ra.index, atomicType); + } + } + + public void setRecMappingAtomType(RecAtom ra, MappingAtomicType atomicType) { + synchronized (this.recListAtoms) { + ra.setKind(Atom.Kind.MAPPING_ATOM); + this.recMappingAtoms.set(ra.index, atomicType); + } + } + + public ListAtomicType getRecListAtomType(RecAtom ra) { + synchronized (this.recListAtoms) { + return this.recListAtoms.get(ra.index); + } + } + + public MappingAtomicType getRecMappingAtomType(RecAtom ra) { + synchronized (this.recMappingAtoms) { + return this.recMappingAtoms.get(ra.index); + } + } + + public static CellAtomicType cellAtomType(Atom atom) { + return (CellAtomicType) ((TypeAtom) atom).atomicType(); + } + + public void addTypeDef(String typeName, SemType semType) { + this.types.put(typeName, semType); + } + + public Map getTypeNameSemTypeMap() { + return new LinkedHashMap<>(this.types); + } + + public int atomCount() { + synchronized (this.atomTable) { + return this.atomTable.size(); + } + } + + // TODO: instead of compact index we should analyze the environment before serialization, but a naive bumping index + // in the BIRTypeWriter created incorrect indexes in the BIR. This is a temporary workaround. + private CompactionData compactionData = null; + + /** + * During type checking we create recursive type atoms that are not parts of the actual ballerina module which will + * not marshalled. This leaves "holes" in the rec atom lists when we unmarshall the BIR, which will then get + * propagated from one module to next. This method will return a new index corrected for such holes. + * + * @param recAtom atom for which you need the corrected index + * @return index corrected for "holes" in rec atom list + */ + public synchronized int compactRecIndex(RecAtom recAtom) { + if (compactionData == null || !compactionData.state().equals(EnvState.from(this))) { + compactionData = compaction(); + } + if (recAtom.index < COMPACT_INDEX) { + return recAtom.index; + } + return switch (recAtom.kind()) { + case LIST_ATOM -> compactionData.listMap().get(recAtom.index()); + case MAPPING_ATOM -> compactionData.mapMap().get(recAtom.index()); + case FUNCTION_ATOM -> compactionData.funcMap().get(recAtom.index()); + case CELL_ATOM, XML_ATOM, DISTINCT_ATOM -> recAtom.index; + }; + } + + private CompactionData compaction() { + EnvState state = EnvState.from(this); + Map listMap = recListCompaction(this.recListAtoms); + Map mapMap = recListCompaction(this.recMappingAtoms); + Map funcMap = recListCompaction(this.recFunctionAtoms); + return new CompactionData(state, listMap, mapMap, funcMap); + } + + private Map recListCompaction(List recAtomList) { + Map map = new HashMap<>(); + int compactIndex = COMPACT_INDEX; + for (int i = COMPACT_INDEX; i < recAtomList.size(); i++) { + if (recAtomList.get(i) != null) { + map.put(i, compactIndex); + compactIndex++; + } + } + return map; + } + + record EnvState(int recListAtomCount, int recMappingAtomCount, int recFunctionAtomCount) { + + public static EnvState from(Env env) { + return new EnvState(env.recListAtomCount(), env.recMappingAtomCount(), + env.recFunctionAtomCount()); + } + } + + private record CompactionData(EnvState state, Map listMap, Map mapMap, + Map funcMap) { + + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/Error.java b/semtypes/src/main/java/io/ballerina/types/Error.java new file mode 100644 index 000000000000..a3577b4c9f38 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/Error.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.BddNode; + +import static io.ballerina.types.BasicTypeCode.BT_ERROR; +import static io.ballerina.types.Core.subtypeData; +import static io.ballerina.types.PredefinedType.BDD_SUBTYPE_RO; +import static io.ballerina.types.PredefinedType.ERROR; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.basicSubtype; +import static io.ballerina.types.RecAtom.createDistinctRecAtom; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; + +/** + * Contain functions found in error.bal file. + * + * @since 2201.8.0 + */ +public class Error { + public static SemType errorDetail(SemType detail) { + SubtypeData mappingSd = subtypeData(detail, BasicTypeCode.BT_MAPPING); + if (mappingSd instanceof AllOrNothingSubtype allOrNothingSubtype) { + if (allOrNothingSubtype.isAllSubtype()) { + return ERROR; + } else { + // XXX This should be reported as an error + return NEVER; + } + } + + SubtypeData sd = bddIntersect((Bdd) mappingSd, BDD_SUBTYPE_RO); + if (sd.equals(BDD_SUBTYPE_RO)) { + return ERROR; + } + return basicSubtype(BT_ERROR, (ProperSubtypeData) sd); + } + + public static SemType errorDistinct(int distinctId) { + assert distinctId >= 0; + BddNode bdd = bddAtom(createDistinctRecAtom(-distinctId - 1)); + return basicSubtype(BT_ERROR, bdd); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/FixedLengthArray.java b/semtypes/src/main/java/io/ballerina/types/FixedLengthArray.java new file mode 100644 index 000000000000..b12b3a08c103 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/FixedLengthArray.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Represent a fixed length semtype member list similar to a tuple. + * The length of the list is `fixedLength`, the last member of the `initial` is repeated to achieve this semantic. + * { initial: [int], fixedLength: 3, } is same as { initial: [int, int, int], fixedLength: 3 } + * { initial: [string, int], fixedLength: 100 } means `int` is repeated 99 times to get a list of 100 members. + * `fixedLength` must be `0` when `inital` is empty and the `fixedLength` must be at least `initial.length()` + * + * @param initial List of semtypes of the members of the fixes length array. If last member is repeated multiple + * times it is included only once. For example for {@code [string, string, int, int]} initial would + * be {@code [string, string, int]} + * @param fixedLength Actual length of the array. For example for {@code [string, string, int, int]} fixedLength would + * be {@code 4} + * @since 2201.8.0 + */ +public record FixedLengthArray(List initial, int fixedLength) { + + public FixedLengthArray { + initial = List.copyOf(initial); + assert fixedLength >= 0; + } + + public static FixedLengthArray from(List initial, int fixedLength) { + return new FixedLengthArray(initial, fixedLength); + } + + public List initial() { + return Collections.unmodifiableList(initial); + } + + public static FixedLengthArray empty() { + return from(new ArrayList<>(), 0); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/FunctionAtomicType.java b/semtypes/src/main/java/io/ballerina/types/FunctionAtomicType.java new file mode 100644 index 000000000000..a4bcfcd4356b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/FunctionAtomicType.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * FunctionAtomicType node. + * + * @param paramType semtype of parameters represented as a tuple + * @param retType semtype of the return value + * @param qualifiers qualifiers of the function + * @param isGeneric atomic type represent a generic (i.e. have parameters/return type with {@code typeParam} annotation) + * @since 2201.8.0 + */ +public record FunctionAtomicType(SemType paramType, SemType retType, SemType qualifiers, boolean isGeneric) + implements AtomicType { + + public static FunctionAtomicType from(SemType paramType, SemType rest, SemType qualifiers) { + return new FunctionAtomicType(paramType, rest, qualifiers, false); + } + + public static FunctionAtomicType genericFrom(SemType paramType, SemType rest, SemType qualifiers) { + return new FunctionAtomicType(paramType, rest, qualifiers, true); + } + + @Override + public Atom.Kind atomKind() { + return Atom.Kind.FUNCTION_ATOM; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/IntSubtypeConstraints.java b/semtypes/src/main/java/io/ballerina/types/IntSubtypeConstraints.java new file mode 100644 index 000000000000..b597b70da8e4 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/IntSubtypeConstraints.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.IntSubtype; + +import static io.ballerina.types.Core.intSubtype; +import static io.ballerina.types.typeops.IntOps.intSubtypeMax; +import static io.ballerina.types.typeops.IntOps.intSubtypeMin; + +/** + * Port of: + *

+ * {@code + * // Constraints on a subtype of `int`. + * public type IntSubtypeConstraints readonly & record {| + * // all values in the subtype are >= min + * int min; + * // all values in the subtype are <= max + * int max; + * // does the subtype contain all values between min and max? + * boolean all; + * |}; + * } + * + * @since 2201.8.0 + */ +public class IntSubtypeConstraints { + final long min; + final long max; + final boolean all; + + private IntSubtypeConstraints(long min, long max, boolean all) { + this.min = min; + this.max = max; + this.all = all; + } + + // Returns `()` if `t` is not a proper, non-empty subtype of `int`. + // i.e. returns `()` if `t` contains all or non of `int`. + public static IntSubtypeConstraints intSubtypeConstraints(SemType t) { + SubtypeData st = intSubtype(t); + // JBUG can't flatten inner if-else + if (st instanceof AllOrNothingSubtype) { + return null; + } else { + IntSubtype ist = (IntSubtype) st; + int len = ist.ranges.length; + return new IntSubtypeConstraints(intSubtypeMin(ist), intSubtypeMax(ist), len == 1); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/IsEmptyOp.java b/semtypes/src/main/java/io/ballerina/types/IsEmptyOp.java similarity index 65% rename from semtypes/src/main/java/io/ballerina/semtype/IsEmptyOp.java rename to semtypes/src/main/java/io/ballerina/types/IsEmptyOp.java index ce410b0d222f..7697284af7b9 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/IsEmptyOp.java +++ b/semtypes/src/main/java/io/ballerina/types/IsEmptyOp.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,17 +11,17 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Interface representing {@code isEmpty} operation. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface IsEmptyOp { - boolean isEmpty(TypeCheckContext tc, SubtypeData t); + boolean isEmpty(Context cx, SubtypeData t); } diff --git a/semtypes/src/main/java/io/ballerina/types/ListAtomicType.java b/semtypes/src/main/java/io/ballerina/types/ListAtomicType.java new file mode 100644 index 000000000000..939d74b0b6fa --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/ListAtomicType.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * ListAtomicType node. + * + * @param members for a given list type this represents the required members + * @param rest for a given list type this represents the rest type. This is NEVER if the list don't have a rest type + * @since 2201.8.0 + */ +public record ListAtomicType(FixedLengthArray members, CellSemType rest) implements AtomicType { + + public ListAtomicType { + assert members != null; + assert rest != null; + } + + public static ListAtomicType from(FixedLengthArray members, CellSemType rest) { + return new ListAtomicType(members, rest); + } + + @Override + public Atom.Kind atomKind() { + return Atom.Kind.LIST_ATOM; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/ListMemberTypes.java b/semtypes/src/main/java/io/ballerina/types/ListMemberTypes.java new file mode 100644 index 000000000000..bb1dea5fb228 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/ListMemberTypes.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.Range; + +import java.util.Collections; +import java.util.List; + +/** + * Holds a pair of SemType list and Range list. + * Note: Member types at the indices that are not contained in `Range` array represent `never. + * The SemTypes in this list are not `never`. + * + * @param ranges Range array + * @param semTypes SemType array + * @since 2201.11.0 + */ +public record ListMemberTypes(List ranges, List semTypes) { + + public ListMemberTypes { + ranges = Collections.unmodifiableList(ranges); + semTypes = Collections.unmodifiableList(semTypes); + } + + @Override + public List ranges() { + return Collections.unmodifiableList(ranges); + } + + @Override + public List semTypes() { + return Collections.unmodifiableList(semTypes); + } + + public static ListMemberTypes from(List ranges, List semTypes) { + assert ranges != null && semTypes != null; + return new ListMemberTypes(ranges, semTypes); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/MappingAlternative.java b/semtypes/src/main/java/io/ballerina/types/MappingAlternative.java new file mode 100644 index 000000000000..dc668ee5163d --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/MappingAlternative.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents MappingAlternative record in core.bal. + * + * @since 2201.8.0 + */ +public class MappingAlternative { + SemType semType; + MappingAtomicType[] pos; + MappingAtomicType[] neg; + + private MappingAlternative(SemType semType, MappingAtomicType[] pos, MappingAtomicType[] neg) { + this.semType = semType; + this.pos = pos; + this.neg = neg; + } + + public MappingAlternative[] mappingAlternatives(Context cx, SemType t) { + if (t instanceof BasicTypeBitSet b) { + if ((b.bitset & PredefinedType.MAPPING.bitset) == 0) { + return new MappingAlternative[]{}; + } else { + return new MappingAlternative[]{ + from(cx, PredefinedType.MAPPING, List.of(), List.of()) + }; + } + } else { + List paths = new ArrayList<>(); + BddPath.bddPaths((Bdd) Core.getComplexSubtypeData((ComplexSemType) t, BasicTypeCode.BT_MAPPING), paths, + BddPath.from()); + List alts = new ArrayList<>(); + for (BddPath bddPath : paths) { + SemType semType = Core.createBasicSemType(BasicTypeCode.BT_MAPPING, bddPath.bdd); + if (!Core.isNever(semType)) { + alts.add(from(cx, semType, bddPath.pos, bddPath.neg)); + } + } + return alts.toArray(new MappingAlternative[]{}); + } + } + + public MappingAlternative from(Context cx, SemType semType, List pos, List neg) { + MappingAtomicType[] p = new MappingAtomicType[pos.size()]; + MappingAtomicType[] n = new MappingAtomicType[neg.size()]; + for (int i = 0; i < pos.size(); i++) { + p[i] = cx.mappingAtomType(pos.get(i)); + } + for (int i = 0; i < neg.size(); i++) { + n[i] = cx.mappingAtomType(neg.get(i)); + } + return new MappingAlternative(semType, p, n); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/MappingAtomicType.java b/semtypes/src/main/java/io/ballerina/types/MappingAtomicType.java new file mode 100644 index 000000000000..aae9c00fa5c1 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/MappingAtomicType.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +// TODO: consider switching arrays to lists so if does the element wise comparison correctly, (or override equals) + +import java.util.Arrays; +import java.util.Objects; + +/** + * MappingAtomicType node. {@code names} and {@code types} fields must be sorted. + * + * @param names names of the required members + * @param types types of the required members + * @param rest for a given mapping type this represents the rest type. This is NEVER if the mapping don't have a rest + * type + * @since 2201.8.0 + */ +public record MappingAtomicType(String[] names, CellSemType[] types, CellSemType rest) implements AtomicType { + + public MappingAtomicType(String[] names, CellSemType[] types, CellSemType rest) { + this.names = Arrays.copyOf(names, names.length); + this.types = Arrays.copyOf(types, names.length); + this.rest = rest; + } + + // TODO: we can replace these with unmodifiable lists + // (which don't create new lists after changing parameters to lists) + public String[] names() { + return Arrays.copyOf(names, names.length); + } + + public CellSemType[] types() { + return Arrays.copyOf(types, types.length); + } + + public static MappingAtomicType from(String[] names, CellSemType[] types, CellSemType rest) { + return new MappingAtomicType(names, types, rest); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (!(o instanceof MappingAtomicType other)) { + return false; + } + return Arrays.equals(names, other.names) && + Arrays.equals(types, other.types) && + Objects.equals(rest, other.rest); + } + + @Override + public int hashCode() { + return Objects.hash(Arrays.hashCode(names), Arrays.hashCode(types), rest); + } + + @Override + public Atom.Kind atomKind() { + return Atom.Kind.MAPPING_ATOM; + } + + @Override + public String toString() { + return "MappingAtomicType{" + + "names=" + Arrays.toString(names) + + ", types=" + Arrays.toString(types) + + ", rest=" + rest + + '}'; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/OpsTable.java b/semtypes/src/main/java/io/ballerina/types/OpsTable.java new file mode 100644 index 000000000000..fae1e15ad486 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/OpsTable.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.typeops.BasicTypeOpsPanicImpl; +import io.ballerina.types.typeops.BooleanOps; +import io.ballerina.types.typeops.CellOps; +import io.ballerina.types.typeops.DecimalOps; +import io.ballerina.types.typeops.ErrorOps; +import io.ballerina.types.typeops.FloatOps; +import io.ballerina.types.typeops.FunctionOps; +import io.ballerina.types.typeops.FutureOps; +import io.ballerina.types.typeops.IntOps; +import io.ballerina.types.typeops.ListOps; +import io.ballerina.types.typeops.MappingOps; +import io.ballerina.types.typeops.ObjectOps; +import io.ballerina.types.typeops.StreamOps; +import io.ballerina.types.typeops.StringOps; +import io.ballerina.types.typeops.TableOps; +import io.ballerina.types.typeops.TypedescOps; +import io.ballerina.types.typeops.XmlOps; + +/** + * Lookup table containing subtype ops for each basic type indexed by basic type code. + * + * @since 2201.8.0 + */ +public class OpsTable { + private static final BasicTypeOpsPanicImpl PANIC_IMPL = new BasicTypeOpsPanicImpl(); + static final BasicTypeOps[] OPS; + + static { + int i = 0; + OPS = new BasicTypeOps[19]; + OPS[i++] = PANIC_IMPL; // nil + OPS[i++] = new BooleanOps(); // boolean + OPS[i++] = new IntOps(); // int + OPS[i++] = new FloatOps(); // float + OPS[i++] = new DecimalOps(); // decimal + OPS[i++] = new StringOps(); // string + OPS[i++] = new ErrorOps(); // error + OPS[i++] = new TypedescOps(); // typedesc + OPS[i++] = PANIC_IMPL; // handle + OPS[i++] = new FunctionOps(); // function + OPS[i++] = PANIC_IMPL; // regexp + OPS[i++] = new FutureOps(); // future + OPS[i++] = new StreamOps(); // stream + OPS[i++] = new ListOps(); // list + OPS[i++] = new MappingOps(); // mapping + OPS[i++] = new TableOps(); // table + OPS[i++] = new XmlOps(); // xml + OPS[i++] = new ObjectOps(); // object + OPS[i] = new CellOps(); // cell + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/PredefinedType.java b/semtypes/src/main/java/io/ballerina/types/PredefinedType.java new file mode 100644 index 000000000000..9815ee12b9e5 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/PredefinedType.java @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; + +import java.util.StringJoiner; + +import static io.ballerina.types.BasicTypeCode.BT_CELL; +import static io.ballerina.types.BasicTypeCode.BT_LIST; +import static io.ballerina.types.BasicTypeCode.BT_MAPPING; +import static io.ballerina.types.BasicTypeCode.BT_OBJECT; +import static io.ballerina.types.BasicTypeCode.BT_TABLE; +import static io.ballerina.types.BasicTypeCode.BT_XML; +import static io.ballerina.types.BasicTypeCode.VT_INHERENTLY_IMMUTABLE; +import static io.ballerina.types.ComplexSemType.createComplexSemType; +import static io.ballerina.types.Core.union; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_COMMENT_RO; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_COMMENT_RW; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_ELEMENT_RO; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_ELEMENT_RW; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_PI_RO; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_PI_RW; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_TEXT; +import static io.ballerina.types.subtypedata.XmlSubtype.xmlSequence; +import static io.ballerina.types.subtypedata.XmlSubtype.xmlSingleton; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; +import static io.ballerina.types.typeops.XmlOps.XML_SUBTYPE_RO; + +/** + * Contain predefined types used for constructing other types. + * + * @since 2201.8.0 + */ +public final class PredefinedType { + + private static final PredefinedTypeEnv predefinedTypeEnv = PredefinedTypeEnv.getInstance(); + public static final BasicTypeBitSet NEVER = basicTypeUnion(0); + public static final BasicTypeBitSet NIL = basicType(BasicTypeCode.BT_NIL); + public static final BasicTypeBitSet BOOLEAN = basicType(BasicTypeCode.BT_BOOLEAN); + public static final BasicTypeBitSet INT = basicType(BasicTypeCode.BT_INT); + public static final BasicTypeBitSet FLOAT = basicType(BasicTypeCode.BT_FLOAT); + public static final BasicTypeBitSet DECIMAL = basicType(BasicTypeCode.BT_DECIMAL); + public static final BasicTypeBitSet STRING = basicType(BasicTypeCode.BT_STRING); + public static final BasicTypeBitSet ERROR = basicType(BasicTypeCode.BT_ERROR); + public static final BasicTypeBitSet LIST = basicType(BasicTypeCode.BT_LIST); + public static final BasicTypeBitSet MAPPING = basicType(BasicTypeCode.BT_MAPPING); + public static final BasicTypeBitSet TABLE = basicType(BasicTypeCode.BT_TABLE); + public static final BasicTypeBitSet CELL = basicType(BT_CELL); + public static final BasicTypeBitSet UNDEF = basicType(BasicTypeCode.BT_UNDEF); + public static final BasicTypeBitSet REGEXP = basicType(BasicTypeCode.BT_REGEXP); + + // matches all functions + public static final BasicTypeBitSet FUNCTION = basicType(BasicTypeCode.BT_FUNCTION); + public static final BasicTypeBitSet TYPEDESC = basicType(BasicTypeCode.BT_TYPEDESC); + public static final BasicTypeBitSet HANDLE = basicType(BasicTypeCode.BT_HANDLE); + + public static final BasicTypeBitSet XML = basicType(BasicTypeCode.BT_XML); + public static final BasicTypeBitSet OBJECT = basicType(BasicTypeCode.BT_OBJECT); + public static final BasicTypeBitSet STREAM = basicType(BasicTypeCode.BT_STREAM); + public static final BasicTypeBitSet FUTURE = basicType(BasicTypeCode.BT_FUTURE); + + // this is SubtypeData|error + public static final BasicTypeBitSet VAL = basicTypeUnion(BasicTypeCode.VT_MASK); + public static final BasicTypeBitSet INNER = BasicTypeBitSet.from(VAL.bitset | UNDEF.bitset); + public static final BasicTypeBitSet ANY = + basicTypeUnion(BasicTypeCode.VT_MASK & ~(1 << BasicTypeCode.BT_ERROR.code)); + + public static final BasicTypeBitSet SIMPLE_OR_STRING = + basicTypeUnion((1 << BasicTypeCode.BT_NIL.code) + | (1 << BasicTypeCode.BT_BOOLEAN.code) + | (1 << BasicTypeCode.BT_INT.code) + | (1 << BasicTypeCode.BT_FLOAT.code) + | (1 << BasicTypeCode.BT_DECIMAL.code) + | (1 << BasicTypeCode.BT_STRING.code)); + + public static final BasicTypeBitSet NUMBER = + basicTypeUnion((1 << BasicTypeCode.BT_INT.code) + | (1 << BasicTypeCode.BT_FLOAT.code) + | (1 << BasicTypeCode.BT_DECIMAL.code)); + public static final SemType BYTE = IntSubtype.intWidthUnsigned(8); + public static final SemType STRING_CHAR = StringSubtype.stringChar(); + + public static final SemType XML_ELEMENT = xmlSingleton(XML_PRIMITIVE_ELEMENT_RO | XML_PRIMITIVE_ELEMENT_RW); + public static final SemType XML_COMMENT = xmlSingleton(XML_PRIMITIVE_COMMENT_RO | XML_PRIMITIVE_COMMENT_RW); + public static final SemType XML_TEXT = xmlSequence(xmlSingleton(XML_PRIMITIVE_TEXT)); + public static final SemType XML_PI = xmlSingleton(XML_PRIMITIVE_PI_RO | XML_PRIMITIVE_PI_RW); + + public static final int BDD_REC_ATOM_READONLY = 0; + // represents both readonly & map and readonly & readonly[] + public static final BddNode BDD_SUBTYPE_RO = bddAtom(RecAtom.createRecAtom(BDD_REC_ATOM_READONLY)); + // represents (map)[] + public static final ComplexSemType MAPPING_RO = basicSubtype(BT_MAPPING, BDD_SUBTYPE_RO); + + public static final CellAtomicType CELL_ATOMIC_VAL = predefinedTypeEnv.cellAtomicVal(); + public static final TypeAtom ATOM_CELL_VAL = predefinedTypeEnv.atomCellVal(); + + public static final CellAtomicType CELL_ATOMIC_NEVER = predefinedTypeEnv.cellAtomicNever(); + public static final TypeAtom ATOM_CELL_NEVER = predefinedTypeEnv.atomCellNever(); + + public static final CellAtomicType CELL_ATOMIC_INNER = predefinedTypeEnv.cellAtomicInner(); + public static final TypeAtom ATOM_CELL_INNER = predefinedTypeEnv.atomCellInner(); + + public static final CellAtomicType CELL_ATOMIC_UNDEF = predefinedTypeEnv.cellAtomicUndef(); + public static final TypeAtom ATOM_CELL_UNDEF = predefinedTypeEnv.atomCellUndef(); + + static final CellSemType CELL_SEMTYPE_INNER = (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_INNER)); + public static final MappingAtomicType MAPPING_ATOMIC_INNER = MappingAtomicType.from( + new String[]{}, new CellSemType[]{}, CELL_SEMTYPE_INNER + ); + public static final ListAtomicType LIST_ATOMIC_INNER = ListAtomicType.from( + FixedLengthArray.empty(), CELL_SEMTYPE_INNER + ); + + public static final CellAtomicType CELL_ATOMIC_INNER_MAPPING = predefinedTypeEnv.cellAtomicInnerMapping(); + public static final TypeAtom ATOM_CELL_INNER_MAPPING = predefinedTypeEnv.atomCellInnerMapping(); + public static final CellSemType CELL_SEMTYPE_INNER_MAPPING = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_INNER_MAPPING) + ); + + public static final ListAtomicType LIST_ATOMIC_MAPPING = predefinedTypeEnv.listAtomicMapping(); + static final TypeAtom ATOM_LIST_MAPPING = predefinedTypeEnv.atomListMapping(); + // represents (map)[] + public static final BddNode LIST_SUBTYPE_MAPPING = bddAtom(ATOM_LIST_MAPPING); + + public static final CellAtomicType CELL_ATOMIC_INNER_MAPPING_RO = predefinedTypeEnv.cellAtomicInnerMappingRO(); + public static final TypeAtom ATOM_CELL_INNER_MAPPING_RO = predefinedTypeEnv.atomCellInnerMappingRO(); + + public static final CellSemType CELL_SEMTYPE_INNER_MAPPING_RO = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_INNER_MAPPING_RO) + ); + + public static final ListAtomicType LIST_ATOMIC_MAPPING_RO = predefinedTypeEnv.listAtomicMappingRO(); + static final TypeAtom ATOM_LIST_MAPPING_RO = predefinedTypeEnv.atomListMappingRO(); + // represents readonly & (map)[] + static final BddNode LIST_SUBTYPE_MAPPING_RO = bddAtom(ATOM_LIST_MAPPING_RO); + + static final CellSemType CELL_SEMTYPE_VAL = (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_VAL)); + static final CellSemType CELL_SEMTYPE_UNDEF = (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_UNDEF)); + private static final TypeAtom ATOM_CELL_OBJECT_MEMBER_KIND = predefinedTypeEnv.atomCellObjectMemberKind(); + static final CellSemType CELL_SEMTYPE_OBJECT_MEMBER_KIND = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_OBJECT_MEMBER_KIND) + ); + + private static final TypeAtom ATOM_CELL_OBJECT_MEMBER_VISIBILITY = + predefinedTypeEnv.atomCellObjectMemberVisibility(); + static final CellSemType CELL_SEMTYPE_OBJECT_MEMBER_VISIBILITY = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_OBJECT_MEMBER_VISIBILITY) + ); + + public static final TypeAtom ATOM_MAPPING_OBJECT_MEMBER = predefinedTypeEnv.atomMappingObjectMember(); + + static final ComplexSemType MAPPING_SEMTYPE_OBJECT_MEMBER = + basicSubtype(BT_MAPPING, bddAtom(ATOM_MAPPING_OBJECT_MEMBER)); + + public static final TypeAtom ATOM_CELL_OBJECT_MEMBER = predefinedTypeEnv.atomCellObjectMember(); + static final CellSemType CELL_SEMTYPE_OBJECT_MEMBER = + (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_OBJECT_MEMBER)); + + static final CellSemType CELL_SEMTYPE_OBJECT_QUALIFIER = CELL_SEMTYPE_VAL; + public static final TypeAtom ATOM_MAPPING_OBJECT = predefinedTypeEnv.atomMappingObject(); + public static final BddNode MAPPING_SUBTYPE_OBJECT = bddAtom(ATOM_MAPPING_OBJECT); + + private static final int BDD_REC_ATOM_OBJECT_READONLY = 1; + + private static final RecAtom OBJECT_RO_REC_ATOM = RecAtom.createRecAtom(BDD_REC_ATOM_OBJECT_READONLY); + + public static final BddNode MAPPING_SUBTYPE_OBJECT_RO = bddAtom(OBJECT_RO_REC_ATOM); + + public static final ComplexSemType MAPPING_ARRAY_RO = basicSubtype(BT_LIST, LIST_SUBTYPE_MAPPING_RO); + public static final TypeAtom ATOM_CELL_MAPPING_ARRAY_RO = predefinedTypeEnv.atomCellMappingArrayRO(); + public static final CellSemType CELL_SEMTYPE_LIST_SUBTYPE_MAPPING_RO = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_MAPPING_ARRAY_RO) + ); + + static final TypeAtom ATOM_LIST_THREE_ELEMENT_RO = predefinedTypeEnv.atomListThreeElementRO(); + // represents [(readonly & map)[], any|error, any|error] + public static final BddNode LIST_SUBTYPE_THREE_ELEMENT_RO = bddAtom(ATOM_LIST_THREE_ELEMENT_RO); + + public static final SemType VAL_READONLY = createComplexSemType(VT_INHERENTLY_IMMUTABLE, + BasicSubtype.from(BT_LIST, BDD_SUBTYPE_RO), + BasicSubtype.from(BT_MAPPING, BDD_SUBTYPE_RO), + BasicSubtype.from(BT_TABLE, LIST_SUBTYPE_THREE_ELEMENT_RO), + BasicSubtype.from(BT_XML, XML_SUBTYPE_RO), + BasicSubtype.from(BT_OBJECT, MAPPING_SUBTYPE_OBJECT_RO) + ); + + public static final SemType INNER_READONLY = union(VAL_READONLY, UNDEF); + public static final CellAtomicType CELL_ATOMIC_INNER_RO = predefinedTypeEnv.cellAtomicInnerRO(); + public static final TypeAtom ATOM_CELL_INNER_RO = predefinedTypeEnv.atomCellInnerRO(); + public static final CellSemType CELL_SEMTYPE_INNER_RO = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_INNER_RO) + ); + public static final TypeAtom ATOM_CELL_VAL_RO = predefinedTypeEnv.atomCellValRO(); + + static final CellSemType CELL_SEMTYPE_VAL_RO = + (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_VAL_RO)); + + public static final TypeAtom ATOM_MAPPING_OBJECT_MEMBER_RO = predefinedTypeEnv.atomMappingObjectMemberRO(); + static final ComplexSemType MAPPING_SEMTYPE_OBJECT_MEMBER_RO = + basicSubtype(BT_MAPPING, bddAtom(ATOM_MAPPING_OBJECT_MEMBER_RO)); + + private static final TypeAtom ATOM_CELL_OBJECT_MEMBER_RO = predefinedTypeEnv.atomCellObjectMemberRO(); + static final CellSemType CELL_SEMTYPE_OBJECT_MEMBER_RO = + (CellSemType) basicSubtype(BT_CELL, bddAtom(ATOM_CELL_OBJECT_MEMBER_RO)); + + public static final ListAtomicType LIST_ATOMIC_TWO_ELEMENT = predefinedTypeEnv.listAtomicTwoElement(); + static final TypeAtom ATOM_LIST_TWO_ELEMENT = predefinedTypeEnv.atomListTwoElement(); + // represents [any|error, any|error] + public static final BddNode LIST_SUBTYPE_TWO_ELEMENT = bddAtom(ATOM_LIST_TWO_ELEMENT); + + public static final ComplexSemType MAPPING_ARRAY = basicSubtype(BT_LIST, LIST_SUBTYPE_MAPPING); + public static final TypeAtom ATOM_CELL_MAPPING_ARRAY = predefinedTypeEnv.atomCellMappingArray(); + public static final CellSemType CELL_SEMTYPE_LIST_SUBTYPE_MAPPING = (CellSemType) basicSubtype( + BT_CELL, bddAtom(ATOM_CELL_MAPPING_ARRAY) + ); + static final TypeAtom ATOM_LIST_THREE_ELEMENT = predefinedTypeEnv.atomListThreeElement(); + // represents [(map)[], any|error, any|error] + public static final BddNode LIST_SUBTYPE_THREE_ELEMENT = bddAtom(ATOM_LIST_THREE_ELEMENT); + + public static final MappingAtomicType MAPPING_ATOMIC_RO = predefinedTypeEnv.mappingAtomicRO(); + public static final MappingAtomicType MAPPING_ATOMIC_OBJECT_RO = predefinedTypeEnv.getMappingAtomicObjectRO(); + + public static final ListAtomicType LIST_ATOMIC_RO = predefinedTypeEnv.listAtomicRO(); + + private PredefinedType() { + } + + // Union of complete basic types + // bits is bit vector indexed by BasicTypeCode + // I would like to make the arg int:Unsigned32 + // but are language/impl bugs that make this not work well + static BasicTypeBitSet basicTypeUnion(int bitset) { + return BasicTypeBitSet.from(bitset); + } + + public static BasicTypeBitSet basicType(BasicTypeCode code) { + return BasicTypeBitSet.from(1 << code.code); + } + + public static ComplexSemType basicSubtype(BasicTypeCode code, ProperSubtypeData data) { + // TODO: We need a more efficient way to do this + if (code.equals(BT_CELL)) { + return CellSemType.from(new ProperSubtypeData[]{data}); + } + return ComplexSemType.createComplexSemType(0, BasicSubtype.from(code, data)); + } + + static String toString(int bitset) { + StringJoiner sj = new StringJoiner("|", Integer.toBinaryString(bitset) + "[", "]"); + + addIfBitSet(sj, bitset, NEVER.bitset, "never"); + addIfBitSet(sj, bitset, NIL.bitset, "nil"); + addIfBitSet(sj, bitset, BOOLEAN.bitset, "boolean"); + addIfBitSet(sj, bitset, INT.bitset, "int"); + addIfBitSet(sj, bitset, FLOAT.bitset, "float"); + addIfBitSet(sj, bitset, DECIMAL.bitset, "decimal"); + addIfBitSet(sj, bitset, STRING.bitset, "string"); + addIfBitSet(sj, bitset, ERROR.bitset, "error"); + addIfBitSet(sj, bitset, TYPEDESC.bitset, "typedesc"); + addIfBitSet(sj, bitset, HANDLE.bitset, "handle"); + addIfBitSet(sj, bitset, FUNCTION.bitset, "function"); + addIfBitSet(sj, bitset, REGEXP.bitset, "regexp"); + addIfBitSet(sj, bitset, FUTURE.bitset, "future"); + addIfBitSet(sj, bitset, STREAM.bitset, "stream"); + addIfBitSet(sj, bitset, LIST.bitset, "list"); + addIfBitSet(sj, bitset, MAPPING.bitset, "map"); + addIfBitSet(sj, bitset, TABLE.bitset, "table"); + addIfBitSet(sj, bitset, XML.bitset, "xml"); + addIfBitSet(sj, bitset, OBJECT.bitset, "object"); + addIfBitSet(sj, bitset, CELL.bitset, "cell"); + addIfBitSet(sj, bitset, UNDEF.bitset, "undef"); + return sj.toString(); + } + + private static void addIfBitSet(StringJoiner sj, int bitset, int bitToBeCheck, String strToBeAdded) { + if ((bitset & bitToBeCheck) != 0) { + sj.add(strToBeAdded); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/PredefinedTypeEnv.java b/semtypes/src/main/java/io/ballerina/types/PredefinedTypeEnv.java new file mode 100644 index 000000000000..7ca357ba04e1 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/PredefinedTypeEnv.java @@ -0,0 +1,645 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicInteger; + +import static io.ballerina.types.Core.union; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_INNER_MAPPING; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_INNER_MAPPING_RO; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_INNER_RO; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_LIST_SUBTYPE_MAPPING; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_LIST_SUBTYPE_MAPPING_RO; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_OBJECT_MEMBER; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_OBJECT_MEMBER_KIND; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_OBJECT_MEMBER_RO; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_OBJECT_MEMBER_VISIBILITY; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_OBJECT_QUALIFIER; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_UNDEF; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_VAL; +import static io.ballerina.types.PredefinedType.CELL_SEMTYPE_VAL_RO; +import static io.ballerina.types.PredefinedType.INNER; +import static io.ballerina.types.PredefinedType.INNER_READONLY; +import static io.ballerina.types.PredefinedType.MAPPING; +import static io.ballerina.types.PredefinedType.MAPPING_ARRAY; +import static io.ballerina.types.PredefinedType.MAPPING_ARRAY_RO; +import static io.ballerina.types.PredefinedType.MAPPING_RO; +import static io.ballerina.types.PredefinedType.MAPPING_SEMTYPE_OBJECT_MEMBER; +import static io.ballerina.types.PredefinedType.MAPPING_SEMTYPE_OBJECT_MEMBER_RO; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.PredefinedType.VAL; +import static io.ballerina.types.PredefinedType.VAL_READONLY; +import static io.ballerina.types.SemTypes.stringConst; +import static io.ballerina.types.TypeAtom.createTypeAtom; + +/** + * This is a utility class used to create various type atoms that needs to be initialized without an environment and + * common to all environments. When we construct an {@code Env}, we can use {@code initializeEnv} to populate it with + * those atoms. + * NOTE: While this class lazy initialize all the atoms technically {@code PredefinedType} will cause it initialize + * all the atoms currently. + * @since 2201.10.0 + */ +public final class PredefinedTypeEnv { + + private PredefinedTypeEnv() { + } + + // Due to some reason spot bug thinks we are returning an array via getInstance(), if this is not public + public static final PredefinedTypeEnv INSTANCE = new PredefinedTypeEnv(); + + public static PredefinedTypeEnv getInstance() { + return INSTANCE; + } + + private final List> initializedCellAtoms = new ArrayList<>(); + private final List> initializedListAtoms = new ArrayList<>(); + private final List> initializedMappingAtoms = new ArrayList<>(); + private final List initializedRecListAtoms = new ArrayList<>(); + private final List initializedRecMappingAtoms = new ArrayList<>(); + private final AtomicInteger nextAtomIndex = new AtomicInteger(0); + + // This is to avoid passing down env argument when doing cell type operations. + // Please refer to the cellSubtypeDataEnsureProper() in cell.bal + private CellAtomicType cellAtomicVal; + private CellAtomicType cellAtomicNever; + + // Represent the typeAtom required to construct equivalent subtypes of map and (any|error)[]. + private CellAtomicType callAtomicInner; + + // TypeAtoms related to (map)[]. This is to avoid passing down env argument when doing + // tableSubtypeComplement operation. + private CellAtomicType cellAtomicInnerMapping; + private ListAtomicType listAtomicMapping; + + // TypeAtoms related to readonly type. This is to avoid requiring context when referring to readonly type. + // CELL_ATOMIC_INNER_MAPPING_RO & LIST_ATOMIC_MAPPING_RO are typeAtoms required to construct + // readonly & (map)[] which is then used for readonly table type when constructing VAL_READONLY + private CellAtomicType cellAtomicInnerMappingRO; + private ListAtomicType listAtomicMappingRO; + private CellAtomicType cellAtomicInnerRO; + private ListAtomicType listAtomicThreeElementRO; + private CellAtomicType cellAtomicMappingArrayRO; + + // TypeAtoms related to [any|error, any|error]. This is to avoid passing down env argument when doing + // streamSubtypeComplement operation. + private CellAtomicType cellAtomicUndef; + private ListAtomicType listAtomicTwoElement; + + // TypeAtoms related to [(map)[], any|error, any|error]. This is to avoid passing down env argument + // when doing tableSubtypeComplement operation. + private CellAtomicType cellAtomicMappingArray; + private ListAtomicType listAtomicThreeElement; + + private CellAtomicType cellAtomicObjectMember; + private CellAtomicType cellAtomicObjectMemberKind; + private CellAtomicType cellAtomicObjectMemberRO; + private CellAtomicType cellAtomicObjectMemberVisibility; + private CellAtomicType cellAtomicValRO; + private ListAtomicType listAtomicRO; + private MappingAtomicType mappingAtomicObject; + private MappingAtomicType mappingAtomicObjectMember; + private MappingAtomicType mappingAtomicObjectMemberRO; + private MappingAtomicType mappingAtomicObjectRO; + private MappingAtomicType mappingAtomicRO; + private TypeAtom atomCellInner; + private TypeAtom atomCellInnerMapping; + private TypeAtom atomCellInnerMappingRO; + private TypeAtom atomCellInnerRO; + private TypeAtom atomCellNever; + private TypeAtom atomCellObjectMember; + private TypeAtom atomCellObjectMemberKind; + private TypeAtom atomCellObjectMemberRO; + private TypeAtom atomCellObjectMemberVisibility; + private TypeAtom atomCellUndef; + private TypeAtom atomCellVal; + private TypeAtom atomCellValRO; + private TypeAtom atomListMapping; + private TypeAtom atomListMappingRO; + private TypeAtom atomListTwoElement; + private TypeAtom atomMappingObject; + private TypeAtom atomMappingObjectMember; + private TypeAtom atomMappingObjectMemberRO; + private TypeAtom atomCellMappingArray; + private TypeAtom atomCellMappingArrayRO; + private TypeAtom atomListThreeElement; + private TypeAtom atomListThreeElementRO; + + private void addInitializedCellAtom(CellAtomicType atom) { + addInitializedAtom(initializedCellAtoms, atom); + } + + private void addInitializedListAtom(ListAtomicType atom) { + addInitializedAtom(initializedListAtoms, atom); + } + + private void addInitializedMapAtom(MappingAtomicType atom) { + addInitializedAtom(initializedMappingAtoms, atom); + } + + private void addInitializedAtom(Collection> atoms, E atom) { + atoms.add(new InitializedTypeAtom<>(atom, nextAtomIndex.getAndIncrement())); + } + + private int cellAtomIndex(CellAtomicType atom) { + return atomIndex(initializedCellAtoms, atom); + } + + private int listAtomIndex(ListAtomicType atom) { + return atomIndex(initializedListAtoms, atom); + } + + private int mappingAtomIndex(MappingAtomicType atom) { + return atomIndex(initializedMappingAtoms, atom); + } + + private int atomIndex(List> initializedAtoms, E atom) { + for (InitializedTypeAtom initializedListAtom : initializedAtoms) { + if (initializedListAtom.atomicType() == atom) { + return initializedListAtom.index(); + } + } + throw new IndexOutOfBoundsException(); + } + + synchronized CellAtomicType cellAtomicVal() { + if (cellAtomicVal == null) { + cellAtomicVal = CellAtomicType.from(VAL, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicVal); + } + return cellAtomicVal; + } + + synchronized TypeAtom atomCellVal() { + if (atomCellVal == null) { + CellAtomicType cellAtomicVal = cellAtomicVal(); + atomCellVal = createTypeAtom(cellAtomIndex(cellAtomicVal), cellAtomicVal); + } + return atomCellVal; + } + + synchronized CellAtomicType cellAtomicNever() { + if (cellAtomicNever == null) { + cellAtomicNever = CellAtomicType.from(NEVER, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicNever); + } + return cellAtomicNever; + } + + synchronized TypeAtom atomCellNever() { + if (atomCellNever == null) { + CellAtomicType cellAtomicNever = cellAtomicNever(); + atomCellNever = createTypeAtom(cellAtomIndex(cellAtomicNever), cellAtomicNever); + } + return atomCellNever; + } + + synchronized CellAtomicType cellAtomicInner() { + if (callAtomicInner == null) { + callAtomicInner = CellAtomicType.from(INNER, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(callAtomicInner); + } + return callAtomicInner; + } + + synchronized TypeAtom atomCellInner() { + if (atomCellInner == null) { + CellAtomicType cellAtomicInner = cellAtomicInner(); + atomCellInner = createTypeAtom(cellAtomIndex(cellAtomicInner), cellAtomicInner); + } + return atomCellInner; + } + + synchronized CellAtomicType cellAtomicInnerMapping() { + if (cellAtomicInnerMapping == null) { + cellAtomicInnerMapping = + CellAtomicType.from(union(MAPPING, UNDEF), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicInnerMapping); + } + return cellAtomicInnerMapping; + } + + synchronized TypeAtom atomCellInnerMapping() { + if (atomCellInnerMapping == null) { + CellAtomicType cellAtomicInnerMapping = cellAtomicInnerMapping(); + atomCellInnerMapping = createTypeAtom(cellAtomIndex(cellAtomicInnerMapping), cellAtomicInnerMapping); + } + return atomCellInnerMapping; + } + + synchronized CellAtomicType cellAtomicInnerMappingRO() { + if (cellAtomicInnerMappingRO == null) { + cellAtomicInnerMappingRO = + CellAtomicType.from(union(MAPPING_RO, UNDEF), CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicInnerMappingRO); + } + return cellAtomicInnerMappingRO; + } + + synchronized TypeAtom atomCellInnerMappingRO() { + if (atomCellInnerMappingRO == null) { + CellAtomicType cellAtomicInnerMappingRO = cellAtomicInnerMappingRO(); + atomCellInnerMappingRO = + createTypeAtom(cellAtomIndex(cellAtomicInnerMappingRO), cellAtomicInnerMappingRO); + } + return atomCellInnerMappingRO; + } + + synchronized ListAtomicType listAtomicMapping() { + if (listAtomicMapping == null) { + listAtomicMapping = ListAtomicType.from( + FixedLengthArray.empty(), CELL_SEMTYPE_INNER_MAPPING + ); + addInitializedListAtom(listAtomicMapping); + } + return listAtomicMapping; + } + + synchronized TypeAtom atomListMapping() { + if (atomListMapping == null) { + ListAtomicType listAtomicMapping = listAtomicMapping(); + atomListMapping = createTypeAtom(listAtomIndex(listAtomicMapping), listAtomicMapping); + } + return atomListMapping; + } + + synchronized ListAtomicType listAtomicMappingRO() { + if (listAtomicMappingRO == null) { + listAtomicMappingRO = ListAtomicType.from(FixedLengthArray.empty(), CELL_SEMTYPE_INNER_MAPPING_RO); + addInitializedListAtom(listAtomicMappingRO); + } + return listAtomicMappingRO; + } + + synchronized TypeAtom atomListMappingRO() { + if (atomListMappingRO == null) { + ListAtomicType listAtomicMappingRO = listAtomicMappingRO(); + atomListMappingRO = createTypeAtom(listAtomIndex(listAtomicMappingRO), listAtomicMappingRO); + } + return atomListMappingRO; + } + + synchronized CellAtomicType cellAtomicInnerRO() { + if (cellAtomicInnerRO == null) { + cellAtomicInnerRO = CellAtomicType.from(INNER_READONLY, CellAtomicType.CellMutability.CELL_MUT_NONE); + addInitializedCellAtom(cellAtomicInnerRO); + } + return cellAtomicInnerRO; + } + + synchronized TypeAtom atomCellInnerRO() { + if (atomCellInnerRO == null) { + CellAtomicType cellAtomicInnerRO = cellAtomicInnerRO(); + atomCellInnerRO = createTypeAtom(cellAtomIndex(cellAtomicInnerRO), cellAtomicInnerRO); + } + return atomCellInnerRO; + } + + synchronized CellAtomicType cellAtomicUndef() { + if (cellAtomicUndef == null) { + cellAtomicUndef = CellAtomicType.from(UNDEF, CellAtomicType.CellMutability.CELL_MUT_NONE); + addInitializedCellAtom(cellAtomicUndef); + } + return cellAtomicUndef; + } + + synchronized TypeAtom atomCellUndef() { + if (atomCellUndef == null) { + CellAtomicType cellAtomicUndef = cellAtomicUndef(); + atomCellUndef = createTypeAtom(cellAtomIndex(cellAtomicUndef), cellAtomicUndef); + } + return atomCellUndef; + } + + synchronized ListAtomicType listAtomicTwoElement() { + if (listAtomicTwoElement == null) { + listAtomicTwoElement = + ListAtomicType.from(FixedLengthArray.from(List.of(CELL_SEMTYPE_VAL), 2), CELL_SEMTYPE_UNDEF); + addInitializedListAtom(listAtomicTwoElement); + } + return listAtomicTwoElement; + } + + synchronized TypeAtom atomListTwoElement() { + if (atomListTwoElement == null) { + ListAtomicType listAtomicTwoElement = listAtomicTwoElement(); + atomListTwoElement = createTypeAtom(listAtomIndex(listAtomicTwoElement), listAtomicTwoElement); + } + return atomListTwoElement; + } + + synchronized CellAtomicType cellAtomicValRO() { + if (cellAtomicValRO == null) { + cellAtomicValRO = CellAtomicType.from( + VAL_READONLY, CellAtomicType.CellMutability.CELL_MUT_NONE + ); + addInitializedCellAtom(cellAtomicValRO); + } + return cellAtomicValRO; + } + + synchronized TypeAtom atomCellValRO() { + if (atomCellValRO == null) { + CellAtomicType cellAtomicValRO = cellAtomicValRO(); + atomCellValRO = createTypeAtom(cellAtomIndex(cellAtomicValRO), cellAtomicValRO); + } + return atomCellValRO; + } + + synchronized MappingAtomicType mappingAtomicObjectMemberRO() { + if (mappingAtomicObjectMemberRO == null) { + mappingAtomicObjectMemberRO = MappingAtomicType.from( + new String[]{"kind", "value", "visibility"}, + new CellSemType[]{CELL_SEMTYPE_OBJECT_MEMBER_KIND, CELL_SEMTYPE_VAL_RO, + CELL_SEMTYPE_OBJECT_MEMBER_VISIBILITY}, + CELL_SEMTYPE_UNDEF); + addInitializedMapAtom(mappingAtomicObjectMemberRO); + } + return mappingAtomicObjectMemberRO; + } + + synchronized TypeAtom atomMappingObjectMemberRO() { + if (atomMappingObjectMemberRO == null) { + MappingAtomicType mappingAtomicObjectMemberRO = mappingAtomicObjectMemberRO(); + atomMappingObjectMemberRO = createTypeAtom(mappingAtomIndex(mappingAtomicObjectMemberRO), + mappingAtomicObjectMemberRO); + } + return atomMappingObjectMemberRO; + } + + synchronized CellAtomicType cellAtomicObjectMemberRO() { + if (cellAtomicObjectMemberRO == null) { + cellAtomicObjectMemberRO = CellAtomicType.from( + MAPPING_SEMTYPE_OBJECT_MEMBER_RO, CellAtomicType.CellMutability.CELL_MUT_NONE + ); + addInitializedCellAtom(cellAtomicObjectMemberRO); + } + return cellAtomicObjectMemberRO; + } + + synchronized TypeAtom atomCellObjectMemberRO() { + if (atomCellObjectMemberRO == null) { + CellAtomicType cellAtomicObjectMemberRO = cellAtomicObjectMemberRO(); + atomCellObjectMemberRO = createTypeAtom(cellAtomIndex(cellAtomicObjectMemberRO), cellAtomicObjectMemberRO); + } + return atomCellObjectMemberRO; + } + + synchronized CellAtomicType cellAtomicObjectMemberKind() { + if (cellAtomicObjectMemberKind == null) { + cellAtomicObjectMemberKind = CellAtomicType.from( + Core.union(stringConst("field"), stringConst("method")), + CellAtomicType.CellMutability.CELL_MUT_NONE + ); + addInitializedCellAtom(cellAtomicObjectMemberKind); + } + return cellAtomicObjectMemberKind; + } + + synchronized TypeAtom atomCellObjectMemberKind() { + if (atomCellObjectMemberKind == null) { + CellAtomicType cellAtomicObjectMemberKind = cellAtomicObjectMemberKind(); + atomCellObjectMemberKind = + createTypeAtom(cellAtomIndex(cellAtomicObjectMemberKind), cellAtomicObjectMemberKind); + } + return atomCellObjectMemberKind; + } + + synchronized CellAtomicType cellAtomicObjectMemberVisibility() { + if (cellAtomicObjectMemberVisibility == null) { + cellAtomicObjectMemberVisibility = CellAtomicType.from( + Core.union(stringConst("public"), stringConst("private")), + CellAtomicType.CellMutability.CELL_MUT_NONE + ); + addInitializedCellAtom(cellAtomicObjectMemberVisibility); + } + return cellAtomicObjectMemberVisibility; + } + + synchronized TypeAtom atomCellObjectMemberVisibility() { + if (atomCellObjectMemberVisibility == null) { + CellAtomicType cellAtomicObjectMemberVisibility = cellAtomicObjectMemberVisibility(); + atomCellObjectMemberVisibility = createTypeAtom(cellAtomIndex(cellAtomicObjectMemberVisibility), + cellAtomicObjectMemberVisibility); + } + return atomCellObjectMemberVisibility; + } + + synchronized MappingAtomicType mappingAtomicObjectMember() { + if (mappingAtomicObjectMember == null) { + mappingAtomicObjectMember = MappingAtomicType.from( + new String[]{"kind", "value", "visibility"}, + new CellSemType[]{CELL_SEMTYPE_OBJECT_MEMBER_KIND, CELL_SEMTYPE_VAL, + CELL_SEMTYPE_OBJECT_MEMBER_VISIBILITY}, + CELL_SEMTYPE_UNDEF); + ; + addInitializedMapAtom(mappingAtomicObjectMember); + } + return mappingAtomicObjectMember; + } + + synchronized TypeAtom atomMappingObjectMember() { + if (atomMappingObjectMember == null) { + MappingAtomicType mappingAtomicObjectMember = mappingAtomicObjectMember(); + atomMappingObjectMember = createTypeAtom(mappingAtomIndex(mappingAtomicObjectMember), + mappingAtomicObjectMember); + } + return atomMappingObjectMember; + } + + synchronized CellAtomicType cellAtomicObjectMember() { + if (cellAtomicObjectMember == null) { + cellAtomicObjectMember = CellAtomicType.from( + MAPPING_SEMTYPE_OBJECT_MEMBER, CellAtomicType.CellMutability.CELL_MUT_UNLIMITED + ); + addInitializedCellAtom(cellAtomicObjectMember); + } + return cellAtomicObjectMember; + } + + synchronized TypeAtom atomCellObjectMember() { + if (atomCellObjectMember == null) { + CellAtomicType cellAtomicObjectMember = cellAtomicObjectMember(); + atomCellObjectMember = createTypeAtom(cellAtomIndex(cellAtomicObjectMember), cellAtomicObjectMember); + } + return atomCellObjectMember; + } + + synchronized MappingAtomicType mappingAtomicObject() { + if (mappingAtomicObject == null) { + mappingAtomicObject = MappingAtomicType.from( + new String[]{"$qualifiers"}, new CellSemType[]{CELL_SEMTYPE_OBJECT_QUALIFIER}, + CELL_SEMTYPE_OBJECT_MEMBER + ); + addInitializedMapAtom(mappingAtomicObject); + } + return mappingAtomicObject; + } + + synchronized TypeAtom atomMappingObject() { + if (atomMappingObject == null) { + MappingAtomicType mappingAtomicObject = mappingAtomicObject(); + atomMappingObject = createTypeAtom(mappingAtomIndex(mappingAtomicObject), mappingAtomicObject); + } + return atomMappingObject; + } + + synchronized ListAtomicType listAtomicRO() { + if (listAtomicRO == null) { + listAtomicRO = ListAtomicType.from(FixedLengthArray.empty(), CELL_SEMTYPE_INNER_RO); + initializedRecListAtoms.add(listAtomicRO); + } + return listAtomicRO; + } + + synchronized MappingAtomicType mappingAtomicRO() { + if (mappingAtomicRO == null) { + mappingAtomicRO = MappingAtomicType.from(new String[]{}, new CellSemType[]{}, CELL_SEMTYPE_INNER_RO); + initializedRecMappingAtoms.add(mappingAtomicRO); + } + return mappingAtomicRO; + } + + synchronized MappingAtomicType getMappingAtomicObjectRO() { + if (mappingAtomicObjectRO == null) { + mappingAtomicObjectRO = MappingAtomicType.from( + new String[]{"$qualifiers"}, new CellSemType[]{CELL_SEMTYPE_OBJECT_QUALIFIER}, + CELL_SEMTYPE_OBJECT_MEMBER_RO + ); + initializedRecMappingAtoms.add(mappingAtomicObjectRO); + } + return mappingAtomicObjectRO; + } + + synchronized CellAtomicType cellAtomicMappingArray() { + if (cellAtomicMappingArray == null) { + cellAtomicMappingArray = CellAtomicType.from(MAPPING_ARRAY, CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicMappingArray); + } + return cellAtomicMappingArray; + } + + synchronized TypeAtom atomCellMappingArray() { + if (atomCellMappingArray == null) { + CellAtomicType cellAtomicMappingArray = cellAtomicMappingArray(); + atomCellMappingArray = createTypeAtom(cellAtomIndex(cellAtomicMappingArray), cellAtomicMappingArray); + } + return atomCellMappingArray; + } + + synchronized CellAtomicType cellAtomicMappingArrayRO() { + if (cellAtomicMappingArrayRO == null) { + cellAtomicMappingArrayRO = CellAtomicType.from(MAPPING_ARRAY_RO, + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + addInitializedCellAtom(cellAtomicMappingArrayRO); + } + return cellAtomicMappingArrayRO; + } + + synchronized TypeAtom atomCellMappingArrayRO() { + if (atomCellMappingArrayRO == null) { + CellAtomicType cellAtomicMappingArrayRO = cellAtomicMappingArrayRO(); + atomCellMappingArrayRO = createTypeAtom(cellAtomIndex(cellAtomicMappingArrayRO), cellAtomicMappingArrayRO); + } + return atomCellMappingArrayRO; + } + + synchronized ListAtomicType listAtomicThreeElement() { + if (listAtomicThreeElement == null) { + listAtomicThreeElement = + ListAtomicType.from(FixedLengthArray.from(List.of(CELL_SEMTYPE_LIST_SUBTYPE_MAPPING, + CELL_SEMTYPE_VAL), 3), + CELL_SEMTYPE_UNDEF); + addInitializedListAtom(listAtomicThreeElement); + } + return listAtomicThreeElement; + } + + synchronized TypeAtom atomListThreeElement() { + if (atomListThreeElement == null) { + ListAtomicType listAtomicThreeElement = listAtomicThreeElement(); + atomListThreeElement = createTypeAtom(listAtomIndex(listAtomicThreeElement), listAtomicThreeElement); + } + return atomListThreeElement; + } + + synchronized ListAtomicType listAtomicThreeElementRO() { + if (listAtomicThreeElementRO == null) { + listAtomicThreeElementRO = + ListAtomicType.from(FixedLengthArray.from(List.of(CELL_SEMTYPE_LIST_SUBTYPE_MAPPING_RO, + CELL_SEMTYPE_VAL), 3), CELL_SEMTYPE_UNDEF); + addInitializedListAtom(listAtomicThreeElementRO); + } + return listAtomicThreeElementRO; + } + + synchronized TypeAtom atomListThreeElementRO() { + if (atomListThreeElementRO == null) { + ListAtomicType listAtomicThreeElementRO = listAtomicThreeElementRO(); + atomListThreeElementRO = createTypeAtom(listAtomIndex(listAtomicThreeElementRO), listAtomicThreeElementRO); + } + return atomListThreeElementRO; + } + + // Due to some reason SpotBug thinks this method is overrideable if we don't put final here as well. + final void initializeEnv(Env env) { + fillRecAtoms(env.recListAtoms, initializedRecListAtoms); + fillRecAtoms(env.recMappingAtoms, initializedRecMappingAtoms); + initializedCellAtoms.forEach(each -> env.cellAtom(each.atomicType())); + initializedListAtoms.forEach(each -> env.listAtom(each.atomicType())); + } + + private void fillRecAtoms(List envRecAtomList, List initializedRecAtoms) { + int count = reservedRecAtomCount(); + for (int i = 0; i < count; i++) { + if (i < initializedRecAtoms.size()) { + envRecAtomList.add(initializedRecAtoms.get(i)); + } else { + // This is mainly to help with bir serialization/deserialization logic. Given the number of such atoms + // will be small this shouldn't be a problem. + envRecAtomList.add(null); + } + } + } + + public int reservedRecAtomCount() { + return Integer.max(initializedRecListAtoms.size(), initializedRecMappingAtoms.size()); + } + + private record InitializedTypeAtom(E atomicType, int index) { + + } + + public Optional getPredefinedRecAtom(int index) { + // NOTE: when adding new reserved rec atoms update the bir.ksy file as well + if (isPredefinedRecAtom(index)) { + return Optional.of(RecAtom.createRecAtom(index)); + } + return Optional.empty(); + } + + public boolean isPredefinedRecAtom(int index) { + return index >= 0 && index < reservedRecAtomCount(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/ProperSubtypeData.java b/semtypes/src/main/java/io/ballerina/types/ProperSubtypeData.java similarity index 69% rename from semtypes/src/main/java/io/ballerina/semtype/ProperSubtypeData.java rename to semtypes/src/main/java/io/ballerina/types/ProperSubtypeData.java index 151c1bd1d133..5ee0e48548db 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/ProperSubtypeData.java +++ b/semtypes/src/main/java/io/ballerina/types/ProperSubtypeData.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,16 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * ProperSubtypeData node. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface ProperSubtypeData extends SubtypeData { } diff --git a/semtypes/src/main/java/io/ballerina/semtype/README.md b/semtypes/src/main/java/io/ballerina/types/README.md similarity index 100% rename from semtypes/src/main/java/io/ballerina/semtype/README.md rename to semtypes/src/main/java/io/ballerina/types/README.md diff --git a/semtypes/src/main/java/io/ballerina/types/RecAtom.java b/semtypes/src/main/java/io/ballerina/types/RecAtom.java new file mode 100644 index 000000000000..6530e43a6588 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/RecAtom.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import static io.ballerina.types.PredefinedType.BDD_REC_ATOM_READONLY; + +/** + * Represent a recursive type atom. + * + * @since 2201.8.0 + */ +public class RecAtom implements Atom { + public final int index; + private Kind targetKind = null; + public static final RecAtom ZERO = new RecAtom(BDD_REC_ATOM_READONLY); + + private RecAtom(int index) { + this.index = index; + } + + private RecAtom(int index, Kind targetKind) { + this.index = index; + this.targetKind = targetKind; + } + + public static RecAtom createRecAtom(int index) { + if (index == BDD_REC_ATOM_READONLY) { + return ZERO; + } + return new RecAtom(index); + } + + public static RecAtom createXMLRecAtom(int index) { + return new RecAtom(index, Kind.XML_ATOM); + } + + public static RecAtom createDistinctRecAtom(int index) { + return new RecAtom(index, Kind.DISTINCT_ATOM); + } + + public void setKind(Kind targetKind) { + this.targetKind = targetKind; + } + + @Override + public int index() { + return index; + } + + @Override + public Kind kind() { + if (targetKind == null) { + throw new IllegalStateException("Target kind is not set for the recursive type atom"); + } + return targetKind; + } + + @Override + public int hashCode() { + if (targetKind == null) { + return index; + } else { + return getIdentifier().hashCode(); + } + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof RecAtom recAtom) { + return recAtom.index == this.index; + } + return false; + } + + @Override + public String toString() { + return "RecAtom{ index=" + index + ", targetKind=" + targetKind + '}'; + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/SemType.java b/semtypes/src/main/java/io/ballerina/types/SemType.java similarity index 68% rename from semtypes/src/main/java/io/ballerina/semtype/SemType.java rename to semtypes/src/main/java/io/ballerina/types/SemType.java index 98bcc7292b55..3b07310dc61e 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/SemType.java +++ b/semtypes/src/main/java/io/ballerina/types/SemType.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,18 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * SemType node. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface SemType { + + int all(); } diff --git a/semtypes/src/main/java/io/ballerina/types/SemTypePair.java b/semtypes/src/main/java/io/ballerina/types/SemTypePair.java new file mode 100644 index 000000000000..1a30f941796a --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/SemTypePair.java @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Holds a pair of semtypes. + * + * @param t1 first semtype + * @param t2 second semtype + * @since 2201.11.0 + */ +public record SemTypePair(SemType t1, SemType t2) { + + public static SemTypePair from(SemType t1, SemType t2) { + assert t1 != null && t2 != null; + return new SemTypePair(t1, t2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/SemTypes.java b/semtypes/src/main/java/io/ballerina/types/SemTypes.java new file mode 100644 index 000000000000..037c9deac740 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/SemTypes.java @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.definition.ObjectDefinition; +import io.ballerina.types.subtypedata.BooleanSubtype; +import io.ballerina.types.subtypedata.DecimalSubtype; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.FutureSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.StringSubtype; +import io.ballerina.types.subtypedata.TableSubtype; +import io.ballerina.types.subtypedata.TypedescSubtype; +import io.ballerina.types.subtypedata.XmlSubtype; +import io.ballerina.types.typeops.ListProj; + +import java.math.BigDecimal; + +/** + * Public API for creating type values. + * + * @since 2201.8.0 + */ +public final class SemTypes { + public static final SemType SINT8 = IntSubtype.intWidthSigned(8); + public static final SemType SINT16 = IntSubtype.intWidthSigned(16); + public static final SemType SINT32 = IntSubtype.intWidthSigned(32); + public static final SemType UINT8 = PredefinedType.BYTE; + public static final SemType UINT16 = IntSubtype.intWidthUnsigned(16); + public static final SemType UINT32 = IntSubtype.intWidthUnsigned(32); + public static final SemType CHAR = PredefinedType.STRING_CHAR; + public static final SemType XML_ELEMENT = PredefinedType.XML_ELEMENT; + public static final SemType XML_COMMENT = PredefinedType.XML_COMMENT; + public static final SemType XML_TEXT = PredefinedType.XML_TEXT; + public static final SemType XML_PI = PredefinedType.XML_PI; + + public static SemType floatConst(double v) { + return FloatSubtype.floatConst(v); + } + + public static SemType intConst(long value) { + return IntSubtype.intConst(value); + } + + public static SemType stringConst(String value) { + return StringSubtype.stringConst(value); + } + + public static SemType booleanConst(boolean value) { + return BooleanSubtype.booleanConst(value); + } + + public static SemType decimalConst(String value) { + if (value.contains("d") || value.contains("D")) { + value = value.substring(0, value.length() - 1); + } + BigDecimal d = new BigDecimal(value); + return DecimalSubtype.decimalConst(d); + } + + public static SemType union(SemType t1, SemType t2) { + return Core.union(t1, t2); + } + + public static SemType union(SemType first, SemType second, SemType... rest) { + SemType u = Core.union(first, second); + for (SemType s : rest) { + u = Core.union(u, s); + } + return u; + } + + public static SemType intersect(SemType t1, SemType t2) { + return Core.intersect(t1, t2); + } + + public static SemType intersect(SemType first, SemType second, SemType... rest) { + SemType i = Core.intersect(first, second); + for (SemType s : rest) { + i = Core.intersect(i, s); + } + return i; + } + + public static boolean isSubtype(Context context, SemType t1, SemType t2) { + return Core.isSubtype(context, t1, t2); + } + + public static boolean isSubtypeSimple(SemType t1, BasicTypeBitSet t2) { + return Core.isSubtypeSimple(t1, t2); + } + + public static boolean isSubtypeSimpleNotNever(SemType t1, BasicTypeBitSet t2) { + return !Core.isNever(t1) && Core.isSubtypeSimple(t1, t2); + } + + public static boolean containsBasicType(SemType t1, BasicTypeBitSet t2) { + return (Core.widenToBasicTypes(t1).bitset & t2.bitset) != 0; + } + + public static boolean containsType(Context context, SemType type, SemType typeToBeContained) { + return Core.isSameType(context, Core.intersect(type, typeToBeContained), typeToBeContained); + } + + public static boolean isSameType(Context context, SemType t1, SemType t2) { + return Core.isSameType(context, t1, t2); + } + + public static SemType errorDetail(SemType detail) { + return Error.errorDetail(detail); + } + + public static SemType errorDistinct(int distinctId) { + return Error.errorDistinct(distinctId); + } + + public static SemType objectDistinct(int distinctId) { + return ObjectDefinition.distinct(distinctId); + } + + public static SemType tableContaining(Env env, SemType tableConstraint) { + return TableSubtype.tableContaining(env, tableConstraint); + } + + public static SemType tableContainingKeySpecifier(Context cx, SemType tableConstraint, String[] fieldNames) { + return TableSubtype.tableContainingKeySpecifier(cx, tableConstraint, fieldNames); + } + + public static SemType tableContainingKeyConstraint(Context cx, SemType tableConstraint, SemType keyConstraint) { + return TableSubtype.tableContainingKeyConstraint(cx, tableConstraint, keyConstraint); + } + + public static SemType futureContaining(Env env, SemType constraint) { + return FutureSubtype.futureContaining(env, constraint); + } + + public static SemType typedescContaining(Env env, SemType constraint) { + return TypedescSubtype.typedescContaining(env, constraint); + } + + public static SemType mappingMemberTypeInnerVal(Context context, SemType t, SemType m) { + return Core.mappingMemberTypeInnerVal(context, t, m); + } + + public static SemType listProj(Context context, SemType t, SemType key) { + return ListProj.listProjInnerVal(context, t, key); + } + + public static SemType listMemberType(Context context, SemType t, SemType key) { + return Core.listMemberTypeInnerVal(context, t, key); + } + + public static SemType xmlSequence(SemType t) { + return XmlSubtype.xmlSequence(t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/SubtypeData.java b/semtypes/src/main/java/io/ballerina/types/SubtypeData.java similarity index 69% rename from semtypes/src/main/java/io/ballerina/semtype/SubtypeData.java rename to semtypes/src/main/java/io/ballerina/types/SubtypeData.java index 8b6834734a85..51d8a9810103 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/SubtypeData.java +++ b/semtypes/src/main/java/io/ballerina/types/SubtypeData.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,16 +11,16 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** * Represent SubtypeData type. * - * @since 2.0.0 + * @since 2201.8.0 */ public interface SubtypeData { } diff --git a/semtypes/src/main/java/io/ballerina/types/TypeAtom.java b/semtypes/src/main/java/io/ballerina/types/TypeAtom.java new file mode 100644 index 000000000000..b83a82364a2c --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/TypeAtom.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +/** + * Represent a TypeAtom. + * + * @param index index of the type atom. This is unique within a given {@code Env}. {@code RecAtom}'s that refer to + * this type atom will also have the same index. + * @param atomicType atomic type representing the actual type represented by this atom. + * @since 2201.8.0 + */ +public record TypeAtom(int index, AtomicType atomicType) implements Atom { + + // Note: Whenever creating a 'TypeAtom', its 'atomicType' needs to be added to the 'Env.atomTable' + public static TypeAtom createTypeAtom(int index, AtomicType atomicType) { + assert index >= 0; + return new TypeAtom(index, atomicType); + } + + @Override + public int hashCode() { + return this.getIdentifier().hashCode(); + } + + @Override + public Kind kind() { + return atomicType.atomKind(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/BddMemo.java b/semtypes/src/main/java/io/ballerina/types/Value.java similarity index 52% rename from semtypes/src/main/java/io/ballerina/semtype/BddMemo.java rename to semtypes/src/main/java/io/ballerina/types/Value.java index 993f1670a76b..6408dd2f3383 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/BddMemo.java +++ b/semtypes/src/main/java/io/ballerina/types/Value.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,22 +11,25 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; +package io.ballerina.types; /** - * Represent BddMomo type used for memoization. + * Represent `Value` type. * - * @since 2.0.0 + * @since 2201.8.0 */ -public class BddMemo { - Bdd bddNode; - MemoStatus isEmpty; +public class Value { + public final Object value; - enum MemoStatus { - NOT_SET, TRUE, FALSE + private Value(Object value) { + this.value = value; + } + + public static Value from(Object value) { + return new Value(value); } } diff --git a/semtypes/src/main/java/io/ballerina/types/definition/CellField.java b/semtypes/src/main/java/io/ballerina/types/definition/CellField.java new file mode 100644 index 000000000000..df4a40fae836 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/CellField.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.CellSemType; + +/** + * Represents a cell field in a mapping type. + * + * @param name name of the field + * @param type cell-sem-type of the field + * @since 2201.10.0 + */ +public record CellField(String name, CellSemType type) { + + public static CellField from(String name, CellSemType type) { + return new CellField(name, type); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/Field.java b/semtypes/src/main/java/io/ballerina/types/definition/Field.java new file mode 100644 index 000000000000..288fc2374e2b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/Field.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.SemType; + +/** + * Represent a field in a mapping type. + * + * @param name field name + * @param ty field type + * @param ro whether the field is readonly + * @param opt whether the field is optional + * @since 2201.10.0 + */ +public record Field(String name, SemType ty, boolean ro, boolean opt) { + + public static Field from(String name, SemType type, boolean ro, boolean opt) { + return new Field(name, type, ro, opt); + } + + @Override + public String toString() { + return "Field[" + + "name=" + name + ", " + + "ty=" + ty + ", " + + "ro=" + ro + ", " + + "opt=" + opt + ']'; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/FunctionDefinition.java b/semtypes/src/main/java/io/ballerina/types/definition/FunctionDefinition.java new file mode 100644 index 000000000000..e1c6c33df23b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/FunctionDefinition.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.FunctionAtomicType; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BddNode; + +import static io.ballerina.types.PredefinedType.basicSubtype; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; + +/** + * Represent function type desc. + * + * @since 2201.8.0 + */ +public final class FunctionDefinition implements Definition { + + private RecAtom rec; + private SemType semType; + + @Override + public SemType getSemType(Env env) { + if (semType != null) { + return semType; + } + RecAtom rec = env.recFunctionAtom(); + this.rec = rec; + return this.createSemType(rec); + } + + private SemType createSemType(Atom rec) { + BddNode bdd = bddAtom(rec); + ComplexSemType s = basicSubtype(BasicTypeCode.BT_FUNCTION, bdd); + this.semType = s; + return s; + } + + public SemType define(Env env, SemType args, SemType ret, FunctionQualifiers qualifiers) { + FunctionAtomicType atomicType = FunctionAtomicType.from(args, ret, qualifiers.semType()); + return defineInternal(env, atomicType); + } + + public SemType defineGeneric(Env env, SemType args, SemType ret, FunctionQualifiers qualifiers) { + FunctionAtomicType atomicType = FunctionAtomicType.genericFrom(args, ret, qualifiers.semType()); + return defineInternal(env, atomicType); + } + + private SemType defineInternal(Env env, FunctionAtomicType atomicType) { + Atom atom; + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecFunctionAtomType(rec, atomicType); + } else { + atom = env.functionAtom(atomicType); + } + return this.createSemType(atom); + } + +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/FunctionQualifiers.java b/semtypes/src/main/java/io/ballerina/types/definition/FunctionQualifiers.java new file mode 100644 index 000000000000..17e73aac75c4 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/FunctionQualifiers.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ + +package io.ballerina.types.definition; + +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; + +import java.util.List; + +/** + * Wrapper class for the semtype representing the {@code function-quals} of a function. + * + * @param semType the semtype representing the function qualifiers + * @since 2201.10.0 + */ +public record FunctionQualifiers(SemType semType) { + + public FunctionQualifiers { + assert semType != null; + assert Core.isSubtypeSimple(semType, PredefinedType.LIST); + } + + public static FunctionQualifiers from(Env env, boolean isolated, boolean transactional) { + return new FunctionQualifiers(createSemType(env, isolated, transactional)); + } + + private static SemType createSemType(Env env, boolean isolated, boolean transactional) { + ListDefinition ld = new ListDefinition(); + return ld.defineListTypeWrapped(env, List.of( + isolated ? SemTypes.booleanConst(true) : PredefinedType.BOOLEAN, + transactional ? PredefinedType.BOOLEAN : SemTypes.booleanConst(false)), + 2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/ListDefinition.java b/semtypes/src/main/java/io/ballerina/types/definition/ListDefinition.java new file mode 100644 index 000000000000..e1b22ed82b1e --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/ListDefinition.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.typeops.BddCommonOps; + +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.Core.isNever; +import static io.ballerina.types.Core.union; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.PredefinedType.basicSubtype; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; + +/** + * Represent list/tuple type desc. + * + * @since 2201.8.0 + */ +public class ListDefinition implements Definition { + + private RecAtom rec = null; + private ComplexSemType semType = null; + + @Override + public SemType getSemType(Env env) { + ComplexSemType s = this.semType; + if (s == null) { + RecAtom rec = env.recListAtom(); + this.rec = rec; + return this.createSemType(env, rec); + } else { + return s; + } + } + + public SemType tupleTypeWrapped(Env env, SemType... members) { + return defineListTypeWrapped(env, List.of(members), members.length); + } + + public SemType tupleTypeWrappedRo(Env env, SemType... members) { + return defineListTypeWrapped(env, List.of(members), members.length, NEVER, CELL_MUT_NONE); + } + + + public SemType defineListTypeWrapped(Env env, List initial, int fixedLength, SemType rest, + CellAtomicType.CellMutability mut) { + assert rest != null; + List initialCells = initial.stream().map(t -> cellContaining(env, t, mut)).toList(); + CellSemType restCell = cellContaining(env, union(rest, UNDEF), isNever(rest) ? CELL_MUT_NONE : mut); + return define(env, initialCells, fixedLength, restCell); + } + + // Overload defineListTypeWrapped method for commonly used default parameter values + public SemType defineListTypeWrapped(Env env, List initial, int size) { + return defineListTypeWrapped(env, initial, size, NEVER, CELL_MUT_LIMITED); + } + + public SemType defineListTypeWrapped(Env env, List initial, int fixedLength, SemType rest) { + return defineListTypeWrapped(env, initial, fixedLength, rest, CELL_MUT_LIMITED); + } + + public SemType defineListTypeWrapped(Env env, SemType rest) { + return defineListTypeWrapped(env, List.of(), 0, rest); + } + + public SemType defineListTypeWrapped(Env env, SemType rest, CellAtomicType.CellMutability mut) { + return defineListTypeWrapped(env, List.of(), 0, rest, mut); + } + + public SemType defineListTypeWrapped(Env env, List initial, SemType rest) { + return defineListTypeWrapped(env, initial, initial.size(), rest, CELL_MUT_LIMITED); + } + + private ComplexSemType define(Env env, List initial, int fixedLength, CellSemType rest) { + assert rest != null; + FixedLengthArray members = fixedLengthNormalize(FixedLengthArray.from(initial, fixedLength)); + ListAtomicType atomicType = ListAtomicType.from(members, rest); + Atom atom; + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecListAtomType(rec, atomicType); + } else { + atom = env.listAtom(atomicType); + } + return this.createSemType(env, atom); + } + + private FixedLengthArray fixedLengthNormalize(FixedLengthArray array) { + List initial = array.initial(); + int i = initial.size() - 1; + if (i <= 0) { + return array; + } + SemType last = initial.get(i); + i -= 1; + while (i >= 0) { + if (last != initial.get(i)) { + break; + } + i -= 1; + } + return FixedLengthArray.from(initial.subList(0, i + 2), array.fixedLength()); + } + + private ComplexSemType createSemType(Env env, Atom atom) { + BddNode bdd = BddCommonOps.bddAtom(atom); + ComplexSemType complexSemType = basicSubtype(BasicTypeCode.BT_LIST, bdd); + this.semType = complexSemType; + return complexSemType; + } + +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/MappingDefinition.java b/semtypes/src/main/java/io/ballerina/types/definition/MappingDefinition.java new file mode 100644 index 000000000000..4c1e7bc0ee6e --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/MappingDefinition.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Core; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.MappingAtomicType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.typeops.BddCommonOps; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.Core.union; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; + +/** + * Represent mapping type desc. + * + * @since 2201.8.0 + */ +public class MappingDefinition implements Definition { + + private RecAtom rec = null; + private SemType semType = null; + + @Override + public SemType getSemType(Env env) { + SemType s = this.semType; + if (s == null) { + RecAtom rec = env.recMappingAtom(); + this.rec = rec; + return this.createSemType(env, rec); + } else { + return s; + } + } + + /** + * This is a deviation from nBallerina. jBallerina considers `record {never x;}` as `never`. + * This method is to support jBallerina behavior. + */ + public void setSemTypeToNever() { + this.semType = PredefinedType.NEVER; + } + + public SemType define(Env env, List fields, CellSemType rest) { + SplitField sfh = splitFields(fields); + MappingAtomicType atomicType = MappingAtomicType.from(sfh.names.toArray(String[]::new), + sfh.types.toArray(CellSemType[]::new), rest); + Atom atom; + RecAtom rec = this.rec; + if (rec != null) { + atom = rec; + env.setRecMappingAtomType(rec, atomicType); + } else { + atom = env.mappingAtom(atomicType); + } + return this.createSemType(env, atom); + } + + public SemType defineMappingTypeWrapped(Env env, List fields, SemType rest) { + return defineMappingTypeWrapped(env, fields, rest, CELL_MUT_LIMITED); + } + + public SemType defineMappingTypeWrapped(Env env, List fields, SemType rest, + CellAtomicType.CellMutability mut) { + List cellFields = new ArrayList<>(fields.size()); + for (Field field : fields) { + SemType ty = field.ty(); + cellFields.add( + CellField.from(field.name(), cellContaining( + env, + field.opt() ? union(ty, UNDEF) : ty, + field.ro() ? CELL_MUT_NONE : mut + )) + ); + } + CellSemType restCell = cellContaining( + env, + union(rest, UNDEF), + Core.isNever(rest) ? CELL_MUT_NONE : mut + ); + return define(env, cellFields, restCell); + } + + private SemType createSemType(Env env, Atom atom) { + BddNode bdd = BddCommonOps.bddAtom(atom); + ComplexSemType s = PredefinedType.basicSubtype(BasicTypeCode.BT_MAPPING, bdd); + this.semType = s; + return s; + } + + private SplitField splitFields(List fields) { + CellField[] sortedFields = fields.toArray(CellField[]::new); + Arrays.sort(sortedFields, Comparator.comparing(MappingDefinition::fieldName)); + List names = new ArrayList<>(); + List types = new ArrayList<>(); + for (CellField field : sortedFields) { + names.add(field.name()); + types.add(field.type()); + } + return SplitField.from(names, types); + } + + private static String fieldName(CellField f) { + return f.name(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/Member.java b/semtypes/src/main/java/io/ballerina/types/definition/Member.java new file mode 100644 index 000000000000..f5d3f98244bb --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/Member.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types.definition; + +import io.ballerina.types.SemType; + +import static io.ballerina.types.SemTypes.stringConst; +import static io.ballerina.types.SemTypes.union; + +/** + * Represent a member of an object type definition. + * + * @param name member name + * @param valueTy member type + * @param kind is member a field or a method + * @param visibility is member private or public + * @param immutable is member readonly. If this is set valueTy must be a subtype of readonly + * @since 2201.10.0 + */ +public record Member(String name, SemType valueTy, Kind kind, Visibility visibility, boolean immutable) { + + public Member { + assert name != null && valueTy != null && kind != null && visibility != null; + } + + // Various "tag" values associated with a member. Each of these tag values must be convertible to a Field in Map + // type for the member + @FunctionalInterface + interface MemberTag { + + Field field(); + } + + public enum Kind implements MemberTag { + Field, + Method; + + // In nBallerina these are stings since they fit small strings. Maybe consider more efficient representation + // for java + private static final Field FIELD = new Field("kind", stringConst("field"), true, false); + private static final Field METHOD = new Field("kind", stringConst("method"), true, false); + + public Field field() { + return switch (this) { + case Field -> FIELD; + case Method -> METHOD; + }; + } + } + + public enum Visibility implements MemberTag { + Public, + Private; + + private static final SemType PUBLIC_TAG = stringConst("public"); + private static final Field PUBLIC = new Field("visibility", PUBLIC_TAG, true, false); + private static final SemType PRIVATE_TAG = stringConst("private"); + private static final Field PRIVATE = new Field("visibility", PRIVATE_TAG, true, false); + static final Field ALL = new Field("visibility", union(PRIVATE_TAG, PUBLIC_TAG), true, false); + + public Field field() { + return switch (this) { + case Public -> PUBLIC; + case Private -> PRIVATE; + }; + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/ObjectDefinition.java b/semtypes/src/main/java/io/ballerina/types/definition/ObjectDefinition.java new file mode 100644 index 000000000000..1ca827f1b038 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/ObjectDefinition.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types.definition; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.Core; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddNode; + +import java.util.Collection; +import java.util.List; +import java.util.stream.Stream; + +import static io.ballerina.types.BasicTypeCode.BT_OBJECT; +import static io.ballerina.types.Core.createBasicSemType; +import static io.ballerina.types.Core.union; +import static io.ballerina.types.PredefinedType.basicSubtype; +import static io.ballerina.types.RecAtom.createDistinctRecAtom; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; + +/** + * Represent object type desc. + * + * @since 2201.10.0 + */ +public final class ObjectDefinition implements Definition { + + private final MappingDefinition mappingDefinition = new MappingDefinition(); + + public static SemType distinct(int distinctId) { + assert distinctId >= 0; + BddNode bdd = bddAtom(createDistinctRecAtom(-distinctId - 1)); + return basicSubtype(BT_OBJECT, bdd); + } + + // Each object type is represented as mapping type (with its basic type set to object) as fallows + // { + // "$qualifiers": { + // boolean isolated, + // "client"|"service" network + // }, + // [field_name]: { + // "field"|"method" kind, + // "public"|"private" visibility, + // VAL value; + // } + // ...{ + // "field" kind, + // "public"|"private" visibility, + // VAL value; + // } | { + // "method" kind, + // "public"|"private" visibility, + // FUNCTION value; + // } + // } + public SemType define(Env env, ObjectQualifiers qualifiers, Collection members) { + assert validataMembers(members); // This should never happen, so let's not run this in production + CellAtomicType.CellMutability mut = qualifiers.readonly() ? CellAtomicType.CellMutability.CELL_MUT_NONE : + CellAtomicType.CellMutability.CELL_MUT_LIMITED; + Stream memberStream = members.stream() + .map(member -> memberField(env, member, mut)); + Stream qualifierStream = Stream.of(qualifiers.field(env)); + SemType mappingType = mappingDefinition.define(env, Stream.concat(memberStream, qualifierStream).toList(), + restMemberType(env, mut, qualifiers.readonly())); + return objectContaining(mappingType); + } + + private static boolean validataMembers(Collection members) { + // Check if there are two members with same name + return members.stream().map(Member::name).distinct().count() == members.size(); + } + + private SemType objectContaining(SemType mappingType) { + SubtypeData bdd = Core.subtypeData(mappingType, BasicTypeCode.BT_MAPPING); + assert bdd instanceof Bdd; + return createBasicSemType(BT_OBJECT, bdd); + } + + private CellSemType restMemberType(Env env, CellAtomicType.CellMutability mut, boolean immutable) { + MappingDefinition fieldDefn = new MappingDefinition(); + SemType fieldMemberType = fieldDefn.defineMappingTypeWrapped( + env, + List.of( + new Field("value", immutable ? PredefinedType.VAL_READONLY : PredefinedType.VAL, + immutable, false), + Member.Kind.Field.field(), + Member.Visibility.ALL + ), + PredefinedType.NEVER); + + MappingDefinition methodDefn = new MappingDefinition(); + SemType methodMemberType = methodDefn.defineMappingTypeWrapped( + env, + List.of( + new Field("value", PredefinedType.FUNCTION, true, false), + Member.Kind.Method.field(), + Member.Visibility.ALL + ), + PredefinedType.NEVER); + return cellContaining(env, union(fieldMemberType, methodMemberType), mut); + } + + private static CellField memberField(Env env, Member member, CellAtomicType.CellMutability mut) { + MappingDefinition md = new MappingDefinition(); + CellAtomicType.CellMutability fieldMut = member.immutable() ? CellAtomicType.CellMutability.CELL_MUT_NONE : mut; + SemType semtype = md.defineMappingTypeWrapped( + env, + List.of( + new Field("value", member.valueTy(), member.immutable(), false), + member.kind().field(), + member.visibility().field() + ), + PredefinedType.NEVER); + return CellField.from(member.name(), cellContaining(env, semtype, fieldMut)); + } + + @Override + public SemType getSemType(Env env) { + return objectContaining(mappingDefinition.getSemType(env)); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/ObjectQualifiers.java b/semtypes/src/main/java/io/ballerina/types/definition/ObjectQualifiers.java new file mode 100644 index 000000000000..4f7bc17fd6a1 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/ObjectQualifiers.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types.definition; + +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; + +import java.util.List; + +import static io.ballerina.types.SemTypes.booleanConst; +import static io.ballerina.types.SemTypes.stringConst; +import static io.ballerina.types.SemTypes.union; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; + +/** + * Represent {@code object-type-quals} in the spec. + * + * @param isolated is object isolated + * @param readonly represent {@code class readonly}. Note this is used to determining "rest" part of the object + * only {@code Member} types must be correctly set as intersection with readonly where + * applicable even with this set to true + * @param networkQualifier is object client, service or none + * @since 2201.10.0 + */ +public record ObjectQualifiers(boolean isolated, boolean readonly, NetworkQualifier networkQualifier) { + + private static final ObjectQualifiers DEFAULT = new ObjectQualifiers(false, false, NetworkQualifier.None); + + public static ObjectQualifiers defaultQualifiers() { + return DEFAULT; + } + + public static ObjectQualifiers from(boolean isolated, boolean readonly, NetworkQualifier networkQualifier) { + if (networkQualifier == NetworkQualifier.None && !isolated) { + return defaultQualifiers(); + } + return new ObjectQualifiers(isolated, readonly, networkQualifier); + } + + public enum NetworkQualifier { + Client, + Service, + None; + + private static final SemType CLIENT_TAG = stringConst("client"); + private static final Field CLIENT = new Field("network", CLIENT_TAG, true, false); + + private static final SemType SERVICE_TAG = stringConst("service"); + private static final Field SERVICE = new Field("network", SERVICE_TAG, true, false); + + // Object can't be both client and service, which is enforced by the enum. We are using a union here so that + // if this is none it matches both + private static final Field NONE = new Field("network", union(CLIENT_TAG, SERVICE_TAG), true, false); + + private Field field() { + return switch (this) { + case Client -> CLIENT; + case Service -> SERVICE; + case None -> NONE; + }; + } + } + + public CellField field(Env env) { + MappingDefinition md = new MappingDefinition(); + Field isolatedField = + new Field("isolated", isolated ? booleanConst(true) : PredefinedType.BOOLEAN, true, false); + Field networkField = networkQualifier.field(); + SemType ty = md.defineMappingTypeWrapped(env, List.of(isolatedField, networkField), PredefinedType.NEVER, + CellAtomicType.CellMutability.CELL_MUT_NONE); + + return CellField.from("$qualifiers", cellContaining(env, ty)); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/SplitField.java b/semtypes/src/main/java/io/ballerina/types/definition/SplitField.java new file mode 100644 index 000000000000..14a56ee7bafa --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/SplitField.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.CellSemType; + +import java.util.List; + +/** + * Holds the [string[], CellSemType[]] return type. + * + * @since 2201.10.0 + */ +public class SplitField { + public final List names; + public final List types; + + private SplitField(List strings, List semTypes) { + this.names = strings; + this.types = semTypes; + } + + public static SplitField from(List strings, List semTypes) { + return new SplitField(strings, semTypes); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/definition/StreamDefinition.java b/semtypes/src/main/java/io/ballerina/types/definition/StreamDefinition.java new file mode 100644 index 000000000000..dc700a5ecad4 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/definition/StreamDefinition.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.definition; + +import io.ballerina.types.Bdd; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.BasicTypeCode.BT_LIST; +import static io.ballerina.types.BasicTypeCode.BT_STREAM; +import static io.ballerina.types.Core.createBasicSemType; +import static io.ballerina.types.Core.subtypeData; + +/** + * Represent stream type desc. + * + * @since 2201.10.0 + */ +public final class StreamDefinition implements Definition { + + private final ListDefinition listDefinition = new ListDefinition(); + + @Override + public SemType getSemType(Env env) { + return streamContaining((listDefinition.getSemType(env))); + } + + public SemType define(Env env, SemType valueTy, SemType completionTy) { + if (PredefinedType.VAL.equals(completionTy) && PredefinedType.VAL.equals(valueTy)) { + return PredefinedType.STREAM; + } + SemType tuple = listDefinition.tupleTypeWrapped(env, valueTy, completionTy); + return streamContaining(tuple); + } + + private static SemType streamContaining(SemType tupleType) { + SubtypeData bdd = subtypeData(tupleType, BT_LIST); + assert bdd instanceof Bdd; + return createBasicSemType(BT_STREAM, bdd); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/AllOrNothingSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/AllOrNothingSubtype.java similarity index 68% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/AllOrNothingSubtype.java rename to semtypes/src/main/java/io/ballerina/types/subtypedata/AllOrNothingSubtype.java index b14487953317..40099994dddc 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/AllOrNothingSubtype.java +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/AllOrNothingSubtype.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,33 +11,36 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype.subtypedata; +package io.ballerina.types.subtypedata; -import io.ballerina.semtype.SubtypeData; +import io.ballerina.types.SubtypeData; /** * A subtype representing either all subtypes or nothing. * This is the Java representation of the `boolean` found in `SubtypeData` type in Ballerina impl. * - * @since 2.0.0 + * @since 2201.8.0 */ public class AllOrNothingSubtype implements SubtypeData { private final boolean isAll; + private static final AllOrNothingSubtype all = new AllOrNothingSubtype(true); + private static final AllOrNothingSubtype nothing = new AllOrNothingSubtype(false); + private AllOrNothingSubtype(boolean isAll) { this.isAll = isAll; } public static AllOrNothingSubtype createAll() { - return new AllOrNothingSubtype(true); + return all; } public static AllOrNothingSubtype createNothing() { - return new AllOrNothingSubtype(false); + return nothing; } public boolean isAllSubtype() { diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddAllOrNothing.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddAllOrNothing.java new file mode 100644 index 000000000000..56ce186ae170 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddAllOrNothing.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Bdd; + +/** + * Represent boolean subtype of Bdd type. + * + * @since 2201.8.0 + */ +public class BddAllOrNothing implements Bdd { + private final boolean isAll; + + private static final BddAllOrNothing all = new BddAllOrNothing(true); + private static final BddAllOrNothing nothing = new BddAllOrNothing(false); + + private BddAllOrNothing(boolean isAll) { + this.isAll = isAll; + } + + public static BddAllOrNothing bddAll() { + return all; + } + + public static BddAllOrNothing bddNothing() { + return nothing; + } + + public boolean isAll() { + return this.isAll; + } + + public boolean isNothing() { + return !this.isAll; + } + + public BddAllOrNothing complement() { + if (isAll) { + return nothing; + } + return all; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + + if (obj instanceof BddAllOrNothing allOrNothing) { + return this.isAll == allOrNothing.isAll; + } + + return false; + } + + @Override + public int hashCode() { + return 0xa11084 + (isAll ? 1 : 0); + } + + @Override + public String toString() { + return Boolean.toString(isAll); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNode.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNode.java new file mode 100644 index 000000000000..c4c61efede1a --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNode.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.org). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Atom; +import io.ballerina.types.Bdd; + +/** + * Internal node of a BDD, which represents a disjunction of conjunctions of atoms. + */ +public interface BddNode extends Bdd { + + static BddNode create(Atom atom, Bdd left, Bdd middle, Bdd right) { + if (isSimpleNode(left, middle, right)) { + return new BddNodeSimple(atom); + } + return new BddNodeImpl(atom, left, middle, right); + } + + private static boolean isSimpleNode(Bdd left, Bdd middle, Bdd right) { + return left instanceof BddAllOrNothing leftNode && leftNode.isAll() && + middle instanceof BddAllOrNothing middleNode && middleNode.isNothing() && + right instanceof BddAllOrNothing rightNode && rightNode.isNothing(); + } + + Atom atom(); + + Bdd left(); + + Bdd middle(); + + Bdd right(); +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeImpl.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeImpl.java new file mode 100644 index 000000000000..f2bea68b01ef --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeImpl.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Atom; +import io.ballerina.types.Bdd; + +/** + * Actual implementation of a generic Bdd node. + * + * @param atom the atom that this node represents + * @param left path that include this node's atom positively + * @param middle path that doesn't include this node's atom + * @param right path that include this node's atom negatively + * @since 2201.10.0 + */ +public record BddNodeImpl(Atom atom, Bdd left, Bdd middle, Bdd right) implements BddNode { + +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeSimple.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeSimple.java new file mode 100644 index 000000000000..7ead5fbeb3f3 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeSimple.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Atom; +import io.ballerina.types.Bdd; + +/** + * Represent a Bdd node that contains a single atom as positive. This is used to reduce the memory overhead of + * BddNodeImpl in representing such nodes + * + * @param atom Atom this node represents + * @since 2201.10.0 + */ +public record BddNodeSimple(Atom atom) implements BddNode { + + @Override + public Bdd left() { + return BddAllOrNothing.bddAll(); + } + + @Override + public Bdd middle() { + return BddAllOrNothing.bddNothing(); + } + + @Override + public Bdd right() { + return BddAllOrNothing.bddNothing(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BooleanSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BooleanSubtype.java new file mode 100644 index 000000000000..4eaa4f4f5e1b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BooleanSubtype.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import java.util.Optional; + +/** + * Represent BooleanSubtype. + * + * @since 2201.8.0 + */ +public class BooleanSubtype implements ProperSubtypeData { + public final boolean value; + + private BooleanSubtype(boolean value) { + this.value = value; + } + + public static BooleanSubtype from(boolean value) { + return new BooleanSubtype(value); + } + + public static boolean booleanSubtypeContains(SubtypeData d, boolean b) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + BooleanSubtype r = (BooleanSubtype) d; + return r.value == b; + } + + public static SemType booleanConst(boolean value) { + BooleanSubtype t = BooleanSubtype.from(value); + return PredefinedType.basicSubtype(BasicTypeCode.BT_BOOLEAN, t); + } + + public static Optional booleanSubtypeSingleValue(SubtypeData d) { + if (d instanceof AllOrNothingSubtype) { + return Optional.empty(); + } + BooleanSubtype b = (BooleanSubtype) d; + return Optional.of(b.value); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/CellSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/CellSubtype.java new file mode 100644 index 000000000000..e7dcae506dd6 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/CellSubtype.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.TypeAtom; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; + +/** + * CellSubType. + * + * @since 2201.10.0 + */ +public class CellSubtype { + + // TODO: cache common cells such as cell containing NEVER + public static CellSemType cellContaining(Env env, SemType ty) { + return cellContaining(env, ty, CELL_MUT_LIMITED); + } + + public static CellSemType roCellContaining(Env env, SemType ty) { + return cellContaining(env, ty, CELL_MUT_NONE); + } + + public static CellSemType cellContaining(Env env, SemType ty, CellAtomicType.CellMutability mut) { + assert Core.isNever(ty) || !Core.isSubtypeSimple(ty, PredefinedType.CELL); + CellAtomicType atomicCell = CellAtomicType.from(ty, mut); + TypeAtom atom = env.cellAtom(atomicCell); + BddNode bdd = bddAtom(atom); + ComplexSemType complexSemType = PredefinedType.basicSubtype(BasicTypeCode.BT_CELL, bdd); + return CellSemType.from(complexSemType.subtypeDataList()); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/CharStringSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/CharStringSubtype.java new file mode 100644 index 000000000000..b8f9a7bcbe18 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/CharStringSubtype.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.EnumerableType; + +/** + * Represent CharStringSubtype. + * + * @since 2201.8.0 + */ +public class CharStringSubtype extends EnumerableSubtype { + + public boolean allowed; + public EnumerableCharString[] values; + + private CharStringSubtype(boolean allowed, EnumerableCharString[] values) { + this.allowed = allowed; + this.values = values; + } + + public static CharStringSubtype from(boolean allowed, EnumerableCharString[] values) { + return new CharStringSubtype(allowed, values); + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public EnumerableType[] values() { + return values; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/DecimalSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/DecimalSubtype.java new file mode 100644 index 000000000000..22b5fb7483c6 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/DecimalSubtype.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.EnumerableDecimal; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.EnumerableType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import java.math.BigDecimal; +import java.util.Optional; +import java.util.StringJoiner; + +/** + * Represent DecimalSubtype. + * + * @since 2201.8.0 + */ +public class DecimalSubtype extends EnumerableSubtype implements ProperSubtypeData { + public boolean allowed; + public EnumerableDecimal[] values; + + private DecimalSubtype(boolean allowed, EnumerableDecimal value) { + this(allowed, new EnumerableDecimal[]{value}); + } + + private DecimalSubtype(boolean allowed, EnumerableDecimal[] values) { + this.allowed = allowed; + this.values = values; + } + + public static SemType decimalConst(BigDecimal value) { + return PredefinedType.basicSubtype(BasicTypeCode.BT_DECIMAL, + new DecimalSubtype(true, EnumerableDecimal.from(value))); + } + + public static Optional decimalSubtypeSingleValue(SubtypeData d) { + if (d instanceof AllOrNothingSubtype) { + return Optional.empty(); + } + + DecimalSubtype f = (DecimalSubtype) d; + if (!f.allowed) { + return Optional.empty(); + } + + EnumerableDecimal[] values = f.values; + if (values.length != 1) { + return Optional.empty(); + } + return Optional.of(values[0].value); + } + + public static boolean decimalSubtypeContains(SubtypeData d, EnumerableDecimal f) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + + DecimalSubtype v = (DecimalSubtype) d; + for (EnumerableType val : v.values) { + if (val.equals(f)) { + return v.allowed; + } + } + return !v.allowed; + } + + public static SubtypeData createDecimalSubtype(boolean allowed, EnumerableDecimal[] values) { + if (values.length == 0) { + return allowed ? AllOrNothingSubtype.createNothing() : AllOrNothingSubtype.createAll(); + } + return new DecimalSubtype(allowed, values); + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public EnumerableType[] values() { + return values; + } + + @Override + public String toString() { + StringJoiner j = new StringJoiner(", ", "DecimalSubtype:" + (allowed ? "allowed[" : "disallowed["), "]"); + for (EnumerableDecimal value : values) { + j.add(String.valueOf(value.value)); + } + return j.toString(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/FloatSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/FloatSubtype.java new file mode 100644 index 000000000000..992b47b0030f --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/FloatSubtype.java @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.EnumerableFloat; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.EnumerableType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import java.util.Optional; +import java.util.StringJoiner; + +/** + * Represent FloatSubtype. + * + * @since 2201.8.0 + */ +public class FloatSubtype extends EnumerableSubtype implements ProperSubtypeData { + public boolean allowed; + public EnumerableFloat[] values; + + private FloatSubtype(boolean allowed, EnumerableFloat value) { + this(allowed, new EnumerableFloat[]{value}); + } + + private FloatSubtype(boolean allowed, EnumerableFloat[] values) { + this.allowed = allowed; + this.values = values; + } + + public static SemType floatConst(double value) { + return PredefinedType.basicSubtype(BasicTypeCode.BT_FLOAT, new FloatSubtype(true, + EnumerableFloat.from(value))); + } + + public static Optional floatSubtypeSingleValue(SubtypeData d) { + if (d instanceof AllOrNothingSubtype) { + return Optional.empty(); + } + + FloatSubtype f = (FloatSubtype) d; + if (!f.allowed) { + return Optional.empty(); + } + + EnumerableFloat[] values = f.values; + if (values.length != 1) { + return Optional.empty(); + } + return Optional.of(values[0].value); + } + + public static boolean floatSubtypeContains(SubtypeData d, EnumerableFloat f) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + + FloatSubtype v = (FloatSubtype) d; + for (EnumerableType val : v.values) { + if (val.equals(f)) { + return v.allowed; + } + } + return !v.allowed; + } + + public static SubtypeData createFloatSubtype(boolean allowed, EnumerableFloat[] values) { + if (values.length == 0) { + return allowed ? AllOrNothingSubtype.createNothing() : AllOrNothingSubtype.createAll(); + } + return new FloatSubtype(allowed, values); + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public EnumerableType[] values() { + return values; + } + + @Override + public String toString() { + StringJoiner j = new StringJoiner(", ", "FloatSubtype:" + (allowed ? "allowed[" : "disallowed["), "]"); + for (EnumerableFloat value : values) { + j.add(String.valueOf(value.value)); + } + return j.toString(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/FutureSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/FutureSubtype.java new file mode 100644 index 000000000000..f7135617ffdf --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/FutureSubtype.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Bdd; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.MappingDefinition; + +import java.util.List; + +import static io.ballerina.types.BasicTypeCode.BT_FUTURE; +import static io.ballerina.types.BasicTypeCode.BT_MAPPING; +import static io.ballerina.types.Core.createBasicSemType; +import static io.ballerina.types.Core.subtypeData; + +/** + * Represent future subtype. + * + * @since 2201.10.0 + */ +public final class FutureSubtype { + + private FutureSubtype() { + } + + public static SemType futureContaining(Env env, SemType constraint) { + if (PredefinedType.VAL.equals(constraint)) { + return PredefinedType.FUTURE; + } + + MappingDefinition mappingDef = new MappingDefinition(); + SemType mappingType = mappingDef.defineMappingTypeWrapped(env, List.of(), constraint); + Bdd bdd = (Bdd) subtypeData(mappingType, BT_MAPPING); + return createBasicSemType(BT_FUTURE, bdd); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/IntSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/IntSubtype.java similarity index 63% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/IntSubtype.java rename to semtypes/src/main/java/io/ballerina/types/subtypedata/IntSubtype.java index 475640212fca..e977b77cdfd5 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/IntSubtype.java +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/IntSubtype.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,29 +11,30 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype.subtypedata; +package io.ballerina.types.subtypedata; -import io.ballerina.semtype.PredefinedType; -import io.ballerina.semtype.ProperSubtypeData; -import io.ballerina.semtype.SemType; -import io.ballerina.semtype.SubtypeData; -import io.ballerina.semtype.UniformTypeCode; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; import java.util.Arrays; import java.util.Optional; +import java.util.StringJoiner; /** * Represent IntSubtype. * - * @since 2.0.0 + * @since 2201.8.0 */ public class IntSubtype implements ProperSubtypeData { - private final Range[] ranges; + public final Range[] ranges; public IntSubtype(Range[] ranges) { this.ranges = Arrays.copyOf(ranges, ranges.length); @@ -48,7 +49,7 @@ public static IntSubtype createSingleRangeSubtype(long min, long max) { } public static SemType intConst(long value) { - return PredefinedType.uniformSubtype(UniformTypeCode.UT_INT, createSingleRangeSubtype(value, value)); + return PredefinedType.basicSubtype(BasicTypeCode.BT_INT, createSingleRangeSubtype(value, value)); } static void validIntWidth(boolean signed, long bits) { @@ -84,17 +85,17 @@ public static SemType intWidthSigned(long bits) { return PredefinedType.INT; } IntSubtype t = createSingleRangeSubtype(-(1L << (bits - 1L)), (1L << (bits - 1L)) - 1L); - return PredefinedType.uniformSubtype(UniformTypeCode.UT_INT, t); + return PredefinedType.basicSubtype(BasicTypeCode.BT_INT, t); } public static SemType intWidthUnsigned(int bits) { validIntWidth(false, bits); IntSubtype t = createSingleRangeSubtype(0L, (1L << bits) - 1L); - return PredefinedType.uniformSubtype(UniformTypeCode.UT_INT, t); + return PredefinedType.basicSubtype(BasicTypeCode.BT_INT, t); } // Widen to UnsignedN - public SubtypeData intSubtypeWidenUnsigned(SubtypeData d) { + public static SubtypeData intSubtypeWidenUnsigned(SubtypeData d) { if (d instanceof AllOrNothingSubtype) { return d; } @@ -105,10 +106,10 @@ public SubtypeData intSubtypeWidenUnsigned(SubtypeData d) { } Range r = v.ranges[v.ranges.length - 1]; - int i = 8; - while (i <= 32) { + long i = 8L; + while (i <= 32L) { if (r.max < (1L << i)) { - IntSubtype w = createSingleRangeSubtype(0L, i); + IntSubtype w = createSingleRangeSubtype(0L, (1L << i) - 1); return w; } i = i * 2; @@ -116,7 +117,7 @@ public SubtypeData intSubtypeWidenUnsigned(SubtypeData d) { return AllOrNothingSubtype.createAll(); } - public Optional intSubtypeSingleValue(SubtypeData d) { + public static Optional intSubtypeSingleValue(SubtypeData d) { if (d instanceof AllOrNothingSubtype) { return Optional.empty(); } @@ -133,16 +134,32 @@ public Optional intSubtypeSingleValue(SubtypeData d) { return Optional.of(min); } - /** - * Int Range node. - */ - static class Range { - final long min; - final long max; + public static boolean intSubtypeContains(SubtypeData d, long n) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + IntSubtype v = (IntSubtype) d; + for (Range r : v.ranges) { + if (r.min <= n && n <= r.max) { + return true; + } + } + return false; + } + + @Override + public String toString() { + StringJoiner j = new StringJoiner(", ", "Int:Range[", "]"); + for (Range r : ranges) { + j.add(minusIndi(r.min) + "-" + minusIndi(r.max)); + } + return j.toString(); + } - public Range(long min, long max) { - this.min = min; - this.max = max; + private String minusIndi(long i) { + if (i < 0) { + return "(" + i + ")"; } + return String.valueOf(i); } } diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/NonCharStringSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/NonCharStringSubtype.java new file mode 100644 index 000000000000..3d686bd4fcc4 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/NonCharStringSubtype.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.EnumerableString; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.EnumerableType; + +/** + * Represent NonCharStringSubtype. + * + * @since 2201.8.0 + */ +public class NonCharStringSubtype extends EnumerableSubtype { + + public boolean allowed; + public EnumerableString[] values; + + private NonCharStringSubtype(boolean allowed, EnumerableString[] values) { + this.allowed = allowed; + this.values = values; + } + + public static NonCharStringSubtype from(boolean allowed, EnumerableString[] values) { + return new NonCharStringSubtype(allowed, values); + } + + @Override + public boolean allowed() { + return allowed; + } + + @Override + public EnumerableType[] values() { + return values; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/Range.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/Range.java new file mode 100644 index 000000000000..f34ca9f3a501 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/Range.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +/** + * Int Range node. + * + * @since 2201.8.0 + */ +public class Range { + public final long min; + public final long max; + + public Range(long min, long max) { + this.min = min; + this.max = max; + } + + public static Range from(long min, long max) { + return new Range(min, max); + } + + @Override + public String toString() { + return "Range[" + min + ", " + max + "]"; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/RangeUnion.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/RangeUnion.java new file mode 100644 index 000000000000..cc7cf4846769 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/RangeUnion.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +/** + * Holds a range if there is a single range representing the union/intersect of r1 and r1. + * status -1 means union/intersect is empty because r1 is before r2, with no overlap + * status 1 means union/intersect is empty because r2 is before r1 with no overlap + * Precondition r1 and r2 are non-empty. + * + * @since 2201.8.0 + */ +public class RangeUnion { + public final int status; // -1, 1, default to zero when there is a range + public final Range range; + + private RangeUnion(int status, Range range) { + this.status = status; + this.range = range; + } + + public static RangeUnion from(int status) { + return new RangeUnion(status, null); + } + + public static RangeUnion from(Range range) { + return new RangeUnion(0, range); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/StringSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/StringSubtype.java new file mode 100644 index 000000000000..edd0c4dad869 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/StringSubtype.java @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableString; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import java.util.Arrays; +import java.util.Optional; + +/** + * Represent StringSubtype. + * + * @since 2201.8.0 + */ +public class StringSubtype implements ProperSubtypeData { + + private static final EnumerableString[] EMPTY_STRING_ARR = {}; + private static final EnumerableCharString[] EMPTY_CHAR_ARR = {}; + CharStringSubtype charData; + NonCharStringSubtype nonCharData; + + public CharStringSubtype getChar() { + return charData; + } + + public NonCharStringSubtype getNonChar() { + return nonCharData; + } + + private StringSubtype(CharStringSubtype charData, NonCharStringSubtype nonCharData) { + this.charData = charData; + this.nonCharData = nonCharData; + } + + public static StringSubtype from(CharStringSubtype chara, NonCharStringSubtype nonChar) { + return new StringSubtype(chara, nonChar); + } + + public static boolean stringSubtypeContains(SubtypeData d, String s) { + if (d instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype(); + } + StringSubtype st = (StringSubtype) d; + CharStringSubtype chara = st.charData; + NonCharStringSubtype nonChar = st.nonCharData; + if (s.length() == 1) { + return Arrays.asList(chara.values).contains(EnumerableCharString.from(s)) == chara.allowed; + } + return Arrays.asList(nonChar.values).contains(EnumerableString.from(s)) == nonChar.allowed; + } + + public static SubtypeData createStringSubtype(CharStringSubtype chara, NonCharStringSubtype nonChar) { + if (chara.values.length == 0 && nonChar.values.length == 0) { + if (!chara.allowed && !nonChar.allowed) { + return AllOrNothingSubtype.createAll(); + } else if (chara.allowed && nonChar.allowed) { + return AllOrNothingSubtype.createNothing(); + } + } + return StringSubtype.from(chara, nonChar); + } + + public static Optional stringSubtypeSingleValue(SubtypeData d) { + if (d instanceof AllOrNothingSubtype) { + return Optional.empty(); + } + StringSubtype st = (StringSubtype) d; + CharStringSubtype chara = st.charData; + NonCharStringSubtype nonChar = st.nonCharData; + int charCount = chara.allowed ? chara.values.length : 2; + int nonCharCount = nonChar.allowed ? nonChar.values.length : 2; + if (charCount + nonCharCount == 1) { + return charCount != 0 ? + Optional.of(chara.values[0].value) : Optional.of(nonChar.values[0].value); + } + return Optional.empty(); + } + + public static SemType stringConst(String value) { + CharStringSubtype chara; + NonCharStringSubtype nonChar; + if (value.codePointCount(0, value.length()) == 1) { + chara = CharStringSubtype.from(true, + new EnumerableCharString[]{EnumerableCharString.from(value)}); + nonChar = NonCharStringSubtype.from(true, EMPTY_STRING_ARR); + } else { + chara = CharStringSubtype.from(true, EMPTY_CHAR_ARR); + nonChar = NonCharStringSubtype.from(true, new EnumerableString[]{EnumerableString.from(value)}); + } + return PredefinedType.basicSubtype(BasicTypeCode.BT_STRING, new StringSubtype(chara, nonChar)); + } + + public static SemType stringChar() { + StringSubtype st = new StringSubtype( + CharStringSubtype.from(false, EMPTY_CHAR_ARR), + NonCharStringSubtype.from(true, EMPTY_STRING_ARR)); + return PredefinedType.basicSubtype(BasicTypeCode.BT_STRING, st); + } + + /** + * Describes the relationship between a StringSubtype and a list of strings + * How the StringSubtype covers the list and vice versa. + * type StringSubtypeListCoverage record {| + * // true if the StringSubtype is a subtype of the type containing the strings in the lists + * boolean isSubtype; + * // contains the index in order of each member of the list that is in the StringSubtype + * int[] indices; + * |}; + */ + public static class StringSubtypeListCoverage { + public final boolean isSubtype; + public final int[] indices; + + public StringSubtypeListCoverage(boolean isSubtype, int[] indices) { + this.isSubtype = isSubtype; + this.indices = indices; + } + + public static StringSubtypeListCoverage from(boolean isSubtype, int[] indices) { + return new StringSubtypeListCoverage(isSubtype, indices); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/TableSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/TableSubtype.java new file mode 100644 index 000000000000..680065c91ff9 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/TableSubtype.java @@ -0,0 +1,113 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.definition.ListDefinition; + +import static io.ballerina.types.BasicTypeCode.BT_LIST; +import static io.ballerina.types.BasicTypeCode.BT_TABLE; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.Core.createBasicSemType; +import static io.ballerina.types.Core.subtypeData; + +/** + * TableSubtype. + * + * @since 2201.8.0 + */ +public final class TableSubtype { + + private TableSubtype() { + } + + public static SemType tableContainingKeyConstraint(Context cx, SemType tableConstraint, SemType keyConstraint) { + SemType normalizedKc; + ListAtomicType lat = Core.listAtomicType(cx, keyConstraint); + if (lat != null && PredefinedType.CELL_ATOMIC_UNDEF.equals(Core.cellAtomicType(lat.rest()))) { + FixedLengthArray members = lat.members(); + normalizedKc = switch (members.fixedLength()) { + case 0 -> PredefinedType.VAL; + case 1 -> Core.cellAtomicType(members.initial().get(0)).ty(); + default -> keyConstraint; + }; + } else { + normalizedKc = keyConstraint; + } + return tableContaining(cx.env, tableConstraint, normalizedKc, PredefinedType.VAL); + } + + public static SemType tableContainingKeySpecifier(Context cx, SemType tableConstraint, String[] fieldNames) { + SemType[] fieldNameSingletons = new SemType[fieldNames.length]; + SemType[] fieldTypes = new SemType[fieldNames.length]; + for (int i = 0; i < fieldNames.length; i++) { + SemType key = SemTypes.stringConst(fieldNames[i]); + fieldNameSingletons[i] = key; + fieldTypes[i] = Core.mappingMemberTypeInnerVal(cx, tableConstraint, key); + } + + SemType normalizedKs = new ListDefinition().tupleTypeWrapped(cx.env, fieldNameSingletons); + + SemType normalizedKc; + if (fieldTypes.length > 1) { + ListDefinition ld = new ListDefinition(); + normalizedKc = ld.tupleTypeWrapped(cx.env, fieldTypes); + } else { + normalizedKc = fieldTypes[0]; + } + return tableContaining(cx.env, tableConstraint, normalizedKc, normalizedKs); + } + + public static SemType tableContaining(Env env, SemType tableConstraint) { + return tableContaining(env, tableConstraint, CELL_MUT_LIMITED); + } + + public static SemType tableContaining(Env env, SemType tableConstraint, CellAtomicType.CellMutability mut) { + SemType normalizedKc = PredefinedType.VAL; // TODO: Ideally this should be anydata + SemType normalizedKs = PredefinedType.VAL; // TODO: Ideally this should be string[] + return tableContaining(env, tableConstraint, normalizedKc, normalizedKs, mut); + } + + private static SemType tableContaining(Env env, SemType tableConstraint, + SemType normalizedKc, SemType normalizedKs, + CellAtomicType.CellMutability mut) { + + assert SemTypes.isSubtypeSimple(tableConstraint, PredefinedType.MAPPING); + ListDefinition typeParamArrDef = new ListDefinition(); + SemType typeParamArray = typeParamArrDef.defineListTypeWrapped(env, tableConstraint, mut); + + ListDefinition listDef = new ListDefinition(); + SemType tupleType = listDef.tupleTypeWrapped(env, typeParamArray, normalizedKc, normalizedKs); + Bdd bdd = (Bdd) subtypeData(tupleType, BT_LIST); + return createBasicSemType(BT_TABLE, bdd); + } + + private static SemType tableContaining(Env env, SemType tableConstraint, + SemType normalizedKc, SemType normalizedKs) { + return tableContaining(env, tableConstraint, normalizedKc, normalizedKs, CELL_MUT_LIMITED); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/TypedescSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/TypedescSubtype.java new file mode 100644 index 000000000000..a56171f854c1 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/TypedescSubtype.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.definition.MappingDefinition; + +import java.util.List; + +import static io.ballerina.types.BasicTypeCode.BT_MAPPING; +import static io.ballerina.types.BasicTypeCode.BT_TYPEDESC; +import static io.ballerina.types.Core.createBasicSemType; +import static io.ballerina.types.Core.subtypeData; + +/** + * Represent typedesc subtype. + * + * @since 2201.10.0 + */ +public final class TypedescSubtype { + + private TypedescSubtype() { + } + + public static SemType typedescContaining(Env env, SemType constraint) { + if (PredefinedType.VAL.equals(constraint)) { + return PredefinedType.TYPEDESC; + } + + MappingDefinition mappingDef = new MappingDefinition(); + SemType mappingType = mappingDef.defineMappingTypeWrapped(env, List.of(), constraint, + CellAtomicType.CellMutability.CELL_MUT_NONE); + Bdd bdd = (Bdd) subtypeData(mappingType, BT_MAPPING); + return createBasicSemType(BT_TYPEDESC, bdd); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/XmlSubtype.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/XmlSubtype.java new file mode 100644 index 000000000000..1991dd0eb86f --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/XmlSubtype.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.subtypedata; + +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.Bdd; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.typeops.BddCommonOps; + +/** + * Implementation specific to basic type xml. + * + * @since 2201.8.0 + */ +public class XmlSubtype implements ProperSubtypeData { + // This is the bitwise-or of above XML_PRIMITIVE_* fields. + // If the XML_PRIMITIVE_NEVER bit is set, then the empty XML sequence belongs to the type. + // If one of the other XML_PRIMITVE_* bits is set, then the type contains the + // corresponding singleton type. + public final int primitives; + // This is a logical combination of the allowed sequences types. The `atom` field of + // the `BddNode` is a bitwise-or of XML_PRIMTIVE_* (except for XML_PRIMITIVE_NEVER). + // It represents a sequence of two or more singletons, where the allowed singletons + // are those whose bit is set in the `atom` field. + public final Bdd sequence; + + public static final int XML_PRIMITIVE_NEVER = 1; + public static final int XML_PRIMITIVE_TEXT = 1 << 1; + public static final int XML_PRIMITIVE_ELEMENT_RO = 1 << 2; + public static final int XML_PRIMITIVE_PI_RO = 1 << 3; + public static final int XML_PRIMITIVE_COMMENT_RO = 1 << 4; + public static final int XML_PRIMITIVE_ELEMENT_RW = 1 << 5; + public static final int XML_PRIMITIVE_PI_RW = 1 << 6; + public static final int XML_PRIMITIVE_COMMENT_RW = 1 << 7; + + public static final int XML_PRIMITIVE_RO_SINGLETON = XML_PRIMITIVE_TEXT | XML_PRIMITIVE_ELEMENT_RO + | XML_PRIMITIVE_PI_RO | XML_PRIMITIVE_COMMENT_RO; + public static final int XML_PRIMITIVE_RO_MASK = XML_PRIMITIVE_NEVER | XML_PRIMITIVE_RO_SINGLETON; + public static final int XML_PRIMITIVE_RW_MASK = XML_PRIMITIVE_ELEMENT_RW | XML_PRIMITIVE_PI_RW + | XML_PRIMITIVE_COMMENT_RW; + public static final int XML_PRIMITIVE_SINGLETON = XML_PRIMITIVE_RO_SINGLETON | XML_PRIMITIVE_RW_MASK; + public static final int XML_PRIMITIVE_ALL_MASK = XML_PRIMITIVE_RO_MASK | XML_PRIMITIVE_RW_MASK; + + private XmlSubtype(int primitives, Bdd sequence) { + this.primitives = primitives; + this.sequence = sequence; + } + + public static XmlSubtype from(int primitives, Bdd sequence) { + return new XmlSubtype(primitives, sequence); + } + + public static SemType xmlSingleton(int primitives) { + return createXmlSemtype(createXmlSubtype(primitives, BddAllOrNothing.bddNothing())); + } + + public static SemType xmlSequence(SemType constituentType) { + // It is a precondition that constituentType is a subtype of XML + assert Core.isSubtypeSimple(constituentType, PredefinedType.XML); + + if (Core.isNever(constituentType)) { + return xmlSequence(xmlSingleton(XML_PRIMITIVE_NEVER)); + } + if (constituentType instanceof BasicTypeBitSet) { + return constituentType; + } else { + ComplexSemType cct = (ComplexSemType) constituentType; + SubtypeData xmlSubtype = Core.getComplexSubtypeData(cct, BasicTypeCode.BT_XML); + xmlSubtype = (xmlSubtype instanceof AllOrNothingSubtype) ? + xmlSubtype : makeXmlSequence((XmlSubtype) xmlSubtype); + return createXmlSemtype(xmlSubtype); + } + } + + private static SubtypeData makeXmlSequence(XmlSubtype d) { + int primitives = XML_PRIMITIVE_NEVER | d.primitives; + int atom = d.primitives & XML_PRIMITIVE_SINGLETON; + Bdd sequence = BddCommonOps.bddUnion(BddCommonOps.bddAtom(RecAtom.createXMLRecAtom(atom)), d.sequence); + return createXmlSubtype(primitives, sequence); + } + + public static SemType createXmlSemtype(SubtypeData xmlSubtype) { + if (xmlSubtype instanceof AllOrNothingSubtype allOrNothingSubtype) { + return allOrNothingSubtype.isAllSubtype() ? PredefinedType.XML : PredefinedType.NEVER; + } else { + return PredefinedType.basicSubtype(BasicTypeCode.BT_XML, (ProperSubtypeData) xmlSubtype); + } + } + + public static SubtypeData createXmlSubtype(int primitives, Bdd sequence) { + int p = primitives & XML_PRIMITIVE_ALL_MASK; + if (sequence instanceof BddAllOrNothing allOrNothing && allOrNothing.isAll() && + p == XML_PRIMITIVE_ALL_MASK) { + return AllOrNothingSubtype.createAll(); + } + return createXmlSubtypeOrEmpty(p, sequence); + } + + public static SubtypeData createXmlSubtypeOrEmpty(int primitives, Bdd sequence) { + if (sequence instanceof BddAllOrNothing allOrNothing && allOrNothing.isNothing() && primitives == 0) { + return AllOrNothingSubtype.createNothing(); + } + return from(primitives, sequence); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/BasicTypeOpsPanicImpl.java b/semtypes/src/main/java/io/ballerina/types/typeops/BasicTypeOpsPanicImpl.java new file mode 100644 index 000000000000..923b7b154f8d --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/BasicTypeOpsPanicImpl.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +/** + * Default implementation for basic subtypes that does not need type-ops. + * + * @since 2201.8.0 + */ +public class BasicTypeOpsPanicImpl implements BasicTypeOps { + @Override + public SubtypeData union(SubtypeData t1, SubtypeData t2) { + throw new IllegalStateException("Binary operation should not be called"); + } + + @Override + public SubtypeData intersect(SubtypeData t1, SubtypeData t2) { + throw new IllegalStateException("Binary operation should not be called"); + } + + @Override + public SubtypeData diff(SubtypeData t1, SubtypeData t2) { + throw new IllegalStateException("Binary operation should not be called"); + } + + @Override + public SubtypeData complement(SubtypeData t) { + throw new IllegalStateException("Unary operation should not be called"); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + throw new IllegalStateException("Unary boolean operation should not be called"); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/BddCommonOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/BddCommonOps.java new file mode 100644 index 000000000000..6de40258eee1 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/BddCommonOps.java @@ -0,0 +1,225 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.Atom; +import io.ballerina.types.Bdd; +import io.ballerina.types.RecAtom; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; + +/** + * Contain common BDD operations found in bdd.bal file. + * + * @since 2201.8.0 + */ +public abstract class BddCommonOps { + + public static BddNode bddAtom(Atom atom) { + return BddNode.create(atom, + BddAllOrNothing.bddAll(), + BddAllOrNothing.bddNothing(), + BddAllOrNothing.bddNothing()); + } + + public static Bdd bddUnion(Bdd b1, Bdd b2) { + if (b1 == b2) { + return b1; + } else if (b1 instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b1).isAll() ? BddAllOrNothing.bddAll() : b2; + } else if (b2 instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b2).isAll() ? BddAllOrNothing.bddAll() : b1; + } else { + BddNode b1Bdd = (BddNode) b1; + BddNode b2Bdd = (BddNode) b2; + long cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0L) { + return bddCreate(b1Bdd.atom(), + b1Bdd.left(), + bddUnion(b1Bdd.middle(), b2), + b1Bdd.right()); + } else if (cmp > 0L) { + return bddCreate(b2Bdd.atom(), + b2Bdd.left(), + bddUnion(b1, b2Bdd.middle()), + b2Bdd.right()); + } else { + return bddCreate(b1Bdd.atom(), + bddUnion(b1Bdd.left(), b2Bdd.left()), + bddUnion(b1Bdd.middle(), b2Bdd.middle()), + bddUnion(b1Bdd.right(), b2Bdd.right())); + } + } + } + + public static Bdd bddIntersect(Bdd b1, Bdd b2) { + if (b1 == b2) { + return b1; + } else if (b1 instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b1).isAll() ? b2 : BddAllOrNothing.bddNothing(); + } else if (b2 instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b2).isAll() ? b1 : BddAllOrNothing.bddNothing(); + } else { + BddNode b1Bdd = (BddNode) b1; + BddNode b2Bdd = (BddNode) b2; + long cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0L) { + return bddCreate(b1Bdd.atom(), + bddIntersect(b1Bdd.left(), b2), + bddIntersect(b1Bdd.middle(), b2), + bddIntersect(b1Bdd.right(), b2)); + } else if (cmp > 0L) { + return bddCreate(b2Bdd.atom(), + bddIntersect(b1, b2Bdd.left()), + bddIntersect(b1, b2Bdd.middle()), + bddIntersect(b1, b2Bdd.right())); + } else { + return bddCreate(b1Bdd.atom(), + bddIntersect( + bddUnion(b1Bdd.left(), b1Bdd.middle()), + bddUnion(b2Bdd.left(), b2Bdd.middle())), + BddAllOrNothing.bddNothing(), + bddIntersect( + bddUnion(b1Bdd.right(), b1Bdd.middle()), + bddUnion(b2Bdd.right(), b2Bdd.middle()))); + } + } + } + + public static Bdd bddDiff(Bdd b1, Bdd b2) { + if (b1 == b2) { + return BddAllOrNothing.bddNothing(); + } else if (b2 instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? BddAllOrNothing.bddNothing() : b1; + } else if (b1 instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? bddComplement(b2) : BddAllOrNothing.bddNothing(); + } else { + BddNode b1Bdd = (BddNode) b1; + BddNode b2Bdd = (BddNode) b2; + long cmp = atomCmp(b1Bdd.atom(), b2Bdd.atom()); + if (cmp < 0L) { + return bddCreate(b1Bdd.atom(), + bddDiff(bddUnion(b1Bdd.left(), b1Bdd.middle()), b2), + BddAllOrNothing.bddNothing(), + bddDiff(bddUnion(b1Bdd.right(), b1Bdd.middle()), b2)); + } else if (cmp > 0L) { + return bddCreate(b2Bdd.atom(), + bddDiff(b1, bddUnion(b2Bdd.left(), b2Bdd.middle())), + BddAllOrNothing.bddNothing(), + bddDiff(b1, bddUnion(b2Bdd.right(), b2Bdd.middle()))); + } else { + // There is an error in the Castagna paper for this formula. + // The union needs to be materialized here. + // The original formula does not work in a case like (a0|a1) - a0. + // Castagna confirms that the following formula is the correct one. + return bddCreate(b1Bdd.atom(), + bddDiff(bddUnion(b1Bdd.left(), b1Bdd.middle()), + bddUnion(b2Bdd.left(), b2Bdd.middle())), + BddAllOrNothing.bddNothing(), + bddDiff(bddUnion(b1Bdd.right(), b1Bdd.middle()), + bddUnion(b2Bdd.right(), b2Bdd.middle()))); + } + } + } + + public static Bdd bddComplement(Bdd b) { + if (b instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b).complement(); + } else { + return bddNodeComplement((BddNode) b); + } + } + + public static Bdd bddNodeComplement(BddNode b) { + BddAllOrNothing bddNothing = BddAllOrNothing.bddNothing(); + if (b.right().equals(bddNothing)) { + return bddCreate(b.atom(), + bddNothing, + bddComplement(bddUnion(b.left(), b.middle())), + bddComplement(b.middle())); + } else if (b.left().equals(bddNothing)) { + return bddCreate(b.atom(), + bddComplement(b.middle()), + bddComplement(bddUnion(b.right(), b.middle())), + bddNothing); + } else if (b.middle().equals(bddNothing)) { + return bddCreate(b.atom(), + bddComplement(b.left()), + bddComplement(bddUnion(b.left(), b.right())), + bddComplement(b.right())); + } else { + // There is a typo in the Frisch PhD thesis for this formula. + // (It has left and right swapped.) + // Castagna (the PhD supervisor) confirms that this is the correct formula. + return bddCreate(b.atom(), + bddComplement(bddUnion(b.left(), b.middle())), + bddNothing, + bddComplement(bddUnion(b.right(), b.middle()))); + } + } + + public static Bdd bddCreate(Atom atom, Bdd left, Bdd middle, Bdd right) { + if (middle instanceof BddAllOrNothing && ((BddAllOrNothing) middle).isAll()) { + return middle; + } + if (left.equals(right)) { + return bddUnion(left, right); + } + + return BddNode.create(atom, left, middle, right); + } + + // order RecAtom < TypeAtom + public static long atomCmp(Atom a1, Atom a2) { + if (a1 instanceof RecAtom r1) { + if (a2 instanceof RecAtom r2) { + return r1.index() - r2.index(); + } else { + return -1L; + } + } else if (a2 instanceof RecAtom) { + return 1L; + } else { + return a1.index() - a2.index(); + } + } + + // This is for debugging purposes. + // It uses the Frisch/Castagna notation. + public static String bddToString(Bdd b, boolean inner) { + if (b instanceof BddAllOrNothing) { + return ((BddAllOrNothing) b).isAll() ? "1" : "0"; + } else { + String str; + BddNode bdd = (BddNode) b; + Atom a = bdd.atom(); + + if (a instanceof RecAtom) { + str = "r" + a; + } else { + str = "a" + a.index(); + } + str += "?" + bddToString(bdd.left(), true) + ":" + bddToString(bdd.middle(), true) + + ":" + bddToString(bdd.right(), true); + if (inner) { + str = "(" + str + ")"; + } + return str; + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/BooleanOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/BooleanOps.java new file mode 100644 index 000000000000..6268a16fc00b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/BooleanOps.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Common; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.BooleanSubtype; + +/** + * Basic type ops for boolean type. + * + * @since 2201.8.0 + */ +public class BooleanOps implements BasicTypeOps { + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + BooleanSubtype v1 = (BooleanSubtype) d1; + BooleanSubtype v2 = (BooleanSubtype) d2; + return v1.value == v2.value ? v1 : AllOrNothingSubtype.createAll(); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + BooleanSubtype v1 = (BooleanSubtype) d1; + BooleanSubtype v2 = (BooleanSubtype) d2; + return v1.value == v2.value ? v1 : AllOrNothingSubtype.createNothing(); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + BooleanSubtype v1 = (BooleanSubtype) d1; + BooleanSubtype v2 = (BooleanSubtype) d2; + return v1.value == v2.value ? AllOrNothingSubtype.createNothing() : v1; + } + + @Override + public SubtypeData complement(SubtypeData d) { + BooleanSubtype v = (BooleanSubtype) d; + BooleanSubtype t = BooleanSubtype.from(!v.value); + return t; + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return Common.notIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/CellOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/CellOps.java new file mode 100644 index 000000000000..9a561c33ed5e --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/CellOps.java @@ -0,0 +1,204 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Common; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; + +import java.util.Collections; +import java.util.EnumSet; + +import static io.ballerina.types.Common.bddSubtypeComplement; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.Common.bddSubtypeIntersect; +import static io.ballerina.types.Common.bddSubtypeUnion; +import static io.ballerina.types.Core.cellAtomType; +import static io.ballerina.types.PredefinedType.ATOM_CELL_NEVER; +import static io.ballerina.types.PredefinedType.ATOM_CELL_VAL; +import static io.ballerina.types.typeops.BddCommonOps.bddAtom; + +/** + * Basic type ops for cell type. + * + * @since 2201.10.0 + */ +public class CellOps extends CommonOps implements BasicTypeOps { + + private static boolean cellFormulaIsEmpty(Context cx, SubtypeData t) { + return Common.bddEvery(cx, (Bdd) t, null, null, CellOps::cellFormulaIsEmpty); + } + + private static boolean cellFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { + CellAtomicType combined; + if (posList == null) { + combined = CellAtomicType.from(PredefinedType.VAL, CellAtomicType.CellMutability.CELL_MUT_UNLIMITED); + } else { + combined = cellAtomType(posList.atom); + Conjunction p = posList.next; + while (p != null) { + combined = intersectCellAtomicType(combined, cellAtomType(p.atom)); + p = p.next; + } + } + return !cellInhabited(cx, combined, negList); + } + + private static boolean cellInhabited(Context cx, CellAtomicType posCell, Conjunction negList) { + SemType pos = posCell.ty(); + if (Core.isEmpty(cx, pos)) { + return false; + } + return switch (posCell.mut()) { + case CELL_MUT_NONE -> cellMutNoneInhabited(cx, pos, negList); + case CELL_MUT_LIMITED -> cellMutLimitedInhabited(cx, pos, negList); + default -> cellMutUnlimitedInhabited(cx, pos, negList); + }; + } + + private static boolean cellMutNoneInhabited(Context cx, SemType pos, Conjunction negList) { + SemType negListUnionResult = cellNegListUnion(negList); + // We expect `isNever` condition to be `true` when there are no negative atoms. + // Otherwise, we do `isEmpty` to conclude on the inhabitance. + return Core.isNever(negListUnionResult) || !Core.isEmpty(cx, Core.diff(pos, negListUnionResult)); + } + + private static SemType cellNegListUnion(Conjunction negList) { + SemType negUnion = PredefinedType.NEVER; + Conjunction neg = negList; + while (neg != null) { + negUnion = Core.union(negUnion, cellAtomType(neg.atom).ty()); + neg = neg.next; + } + return negUnion; + } + + private static boolean cellMutLimitedInhabited(Context cx, SemType pos, Conjunction negList) { + if (negList == null) { + return true; + } + CellAtomicType negAtomicCell = cellAtomType(negList.atom); + if ((negAtomicCell.mut().compareTo(CellAtomicType.CellMutability.CELL_MUT_LIMITED) >= 0) && + Core.isEmpty(cx, Core.diff(pos, negAtomicCell.ty()))) { + return false; + } + return cellMutLimitedInhabited(cx, pos, negList.next); + } + + private static boolean cellMutUnlimitedInhabited(Context cx, SemType pos, Conjunction negList) { + Conjunction neg = negList; + while (neg != null) { + if (cellAtomType(neg.atom).mut() == CellAtomicType.CellMutability.CELL_MUT_LIMITED && + Core.isSameType(cx, PredefinedType.VAL, cellAtomType(neg.atom).ty())) { + return false; + } + neg = neg.next; + } + SemType negListUnionResult = cellNegListUnlimitedUnion(negList); + // We expect `isNever` condition to be `true` when there are no negative atoms with unlimited mutability. + // Otherwise, we do `isEmpty` to conclude on the inhabitance. + return Core.isNever(negListUnionResult) || !Core.isEmpty(cx, Core.diff(pos, negListUnionResult)); + } + + private static SemType cellNegListUnlimitedUnion(Conjunction negList) { + SemType negUnion = PredefinedType.NEVER; + Conjunction neg = negList; + while (neg != null) { + if (cellAtomType(neg.atom).mut() == CellAtomicType.CellMutability.CELL_MUT_UNLIMITED) { + negUnion = Core.union(negUnion, cellAtomType(neg.atom).ty()); + } + neg = neg.next; + } + return negUnion; + } + + public static CellAtomicType intersectCellAtomicType(CellAtomicType c1, CellAtomicType c2) { + SemType ty = Core.intersect(c1.ty(), c2.ty()); + CellAtomicType.CellMutability mut = Collections.min(EnumSet.of(c1.mut(), c2.mut())); + return CellAtomicType.from(ty, mut); + } + + private static ProperSubtypeData cellSubtypeUnion(SubtypeData t1, SubtypeData t2) { + return cellSubtypeDataEnsureProper(bddSubtypeUnion(t1, t2)); + } + + private static ProperSubtypeData cellSubtypeIntersect(SubtypeData t1, SubtypeData t2) { + return cellSubtypeDataEnsureProper(bddSubtypeIntersect(t1, t2)); + } + + private static ProperSubtypeData cellSubtypeDiff(SubtypeData t1, SubtypeData t2) { + return cellSubtypeDataEnsureProper(bddSubtypeDiff(t1, t2)); + } + + private static ProperSubtypeData cellSubtypeComplement(SubtypeData t) { + return cellSubtypeDataEnsureProper(bddSubtypeComplement(t)); + } + + /** + * SubtypeData being a boolean would result in a BasicTypeBitSet which could either be 0 or 1 << BT_CELL. + * This is to avoid cell type being a BasicTypeBitSet, as we don't want to lose the cell wrapper. + */ + private static ProperSubtypeData cellSubtypeDataEnsureProper(SubtypeData subtypeData) { + if (subtypeData instanceof AllOrNothingSubtype allOrNothingSubtype) { + Atom atom; + if (allOrNothingSubtype.isAllSubtype()) { + atom = ATOM_CELL_VAL; + } else { + atom = ATOM_CELL_NEVER; + } + return bddAtom(atom); + } else { + return (ProperSubtypeData) subtypeData; + } + } + + @Override + public SubtypeData union(SubtypeData t1, SubtypeData t2) { + return cellSubtypeUnion(t1, t2); + } + + @Override + public SubtypeData intersect(SubtypeData t1, SubtypeData t2) { + return cellSubtypeIntersect(t1, t2); + } + + @Override + public SubtypeData diff(SubtypeData t1, SubtypeData t2) { + return cellSubtypeDiff(t1, t2); + } + + @Override + public SubtypeData complement(SubtypeData t) { + return cellSubtypeComplement(t); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return cellFormulaIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/typeops/CommonOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/CommonOps.java similarity index 57% rename from semtypes/src/main/java/io/ballerina/semtype/typeops/CommonOps.java rename to semtypes/src/main/java/io/ballerina/types/typeops/CommonOps.java index 328686d429ef..30254d0a12ab 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/typeops/CommonOps.java +++ b/semtypes/src/main/java/io/ballerina/types/typeops/CommonOps.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -11,38 +11,39 @@ * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the + * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype.typeops; +package io.ballerina.types.typeops; -import io.ballerina.semtype.CommonUniformTypeOps; -import io.ballerina.semtype.SubtypeData; +import io.ballerina.types.Bdd; +import io.ballerina.types.CommonBasicTypeOps; +import io.ballerina.types.SubtypeData; /** * Common methods operate on SubtypeData. * - * @since 2.0.0 + * @since 2201.8.0 */ -public abstract class CommonOps implements CommonUniformTypeOps { +public abstract class CommonOps implements CommonBasicTypeOps { @Override public SubtypeData union(SubtypeData t1, SubtypeData t2) { - throw new AssertionError(); + return BddCommonOps.bddUnion((Bdd) t1, (Bdd) t2); } @Override public SubtypeData intersect(SubtypeData t1, SubtypeData t2) { - throw new AssertionError(); + return BddCommonOps.bddIntersect((Bdd) t1, (Bdd) t2); } @Override public SubtypeData diff(SubtypeData t1, SubtypeData t2) { - throw new AssertionError(); + return BddCommonOps.bddDiff((Bdd) t1, (Bdd) t2); } @Override public SubtypeData complement(SubtypeData t) { - throw new AssertionError(); + return BddCommonOps.bddComplement((Bdd) t); } } diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/DecimalOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/DecimalOps.java new file mode 100644 index 000000000000..9e5fb7ff7fe4 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/DecimalOps.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Common; +import io.ballerina.types.Context; +import io.ballerina.types.EnumerableDecimal; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.DecimalSubtype; + +import java.util.ArrayList; + +/** + * Decimal specific methods operate on SubtypeData. + * + * @since 2201.8.0 + */ +public class DecimalOps extends CommonOps implements BasicTypeOps { + @Override + public SubtypeData union(SubtypeData t1, SubtypeData t2) { + ArrayList values = new ArrayList<>(); + boolean allowed = EnumerableSubtype.enumerableSubtypeUnion((DecimalSubtype) t1, (DecimalSubtype) t2, values); + EnumerableDecimal[] valueArray = new EnumerableDecimal[values.size()]; + return DecimalSubtype.createDecimalSubtype(allowed, values.toArray(valueArray)); + } + + @Override + public SubtypeData intersect(SubtypeData t1, SubtypeData t2) { + ArrayList values = new ArrayList<>(); + boolean allowed = EnumerableSubtype.enumerableSubtypeIntersect((DecimalSubtype) t1, + (DecimalSubtype) t2, values); + return DecimalSubtype.createDecimalSubtype(allowed, values.toArray(new EnumerableDecimal[]{})); + } + + @Override + public SubtypeData diff(SubtypeData t1, SubtypeData t2) { + return intersect(t1, complement(t2)); + } + + @Override + public SubtypeData complement(SubtypeData t) { + DecimalSubtype s = (DecimalSubtype) t; + return DecimalSubtype.createDecimalSubtype(!s.allowed, (EnumerableDecimal[]) s.values); + } + + @Override + public boolean isEmpty(Context tc, SubtypeData t) { + return Common.notIsEmpty(tc, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/ErrorOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/ErrorOps.java new file mode 100644 index 000000000000..654d36988c50 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/ErrorOps.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.Common.bddEveryPositive; +import static io.ballerina.types.Common.bddPosMaybeEmpty; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.Common.memoSubtypeIsEmpty; +import static io.ballerina.types.PredefinedType.BDD_SUBTYPE_RO; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; + +/** + * Basic type ops for error type. + * + * @since 2201.8.0 + */ +public class ErrorOps extends CommonOps implements BasicTypeOps { + + private static SubtypeData errorSubtypeComplement(SubtypeData t) { + return bddSubtypeDiff(BDD_SUBTYPE_RO, t); + } + + private static boolean errorSubtypeIsEmpty(Context cx, SubtypeData t) { + Bdd b = (Bdd) t; + // The goal of this is to ensure that mappingFormulaIsEmpty call in errorBddIsEmpty beneath + // does not get an empty posList, because it will interpret that + // as `map` rather than `readonly & map`. + b = bddPosMaybeEmpty(b) ? bddIntersect(b, BDD_SUBTYPE_RO) : b; + return memoSubtypeIsEmpty(cx, cx.mappingMemo, ErrorOps::errorBddIsEmpty, b); + } + + private static boolean errorBddIsEmpty(Context cx, Bdd b) { + return bddEveryPositive(cx, b, null, null, MappingOps::mappingFormulaIsEmpty); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return errorSubtypeComplement(d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return errorSubtypeIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/FieldPair.java b/semtypes/src/main/java/io/ballerina/types/typeops/FieldPair.java new file mode 100644 index 000000000000..7f93864922e2 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/FieldPair.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.CellSemType; + +/** + * Represent the FieldPair record. + * + * @param name name of the field + * @param type1 type of the field in the first mapping + * @param type2 type of the field in the second mapping + * @param index1 index of the field in the first mapping + * @param index2 index of the field in the second mapping + * @since 2201.10.0 + */ +public record FieldPair(String name, CellSemType type1, CellSemType type2, Integer index1, Integer index2) { + + public static FieldPair create(String name, CellSemType type1, CellSemType type2, Integer index1, + Integer index2) { + return new FieldPair(name, type1, type2, index1, index2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/FieldPairs.java b/semtypes/src/main/java/io/ballerina/types/typeops/FieldPairs.java new file mode 100644 index 000000000000..bd85a0f99082 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/FieldPairs.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.MappingAtomicType; + +import java.util.Iterator; + +/** + * Ballerina iterator is similar to an iterable in Java. + * This class implements the iterable for `MappingPairing` + * + * @since 2201.8.0 + */ +public class FieldPairs implements Iterable { + + MappingAtomicType m1; + MappingAtomicType m2; + + public FieldPairs(MappingAtomicType m1, MappingAtomicType m2) { + this.m1 = m1; + this.m2 = m2; + } + + @Override + public Iterator iterator() { + return new MappingPairIterator(m1, m2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/FloatOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/FloatOps.java new file mode 100644 index 000000000000..1264bbcf9671 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/FloatOps.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Common; +import io.ballerina.types.Context; +import io.ballerina.types.EnumerableFloat; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.FloatSubtype; + +import java.util.ArrayList; + +/** + * Float specific methods operate on SubtypeData. + * + * @since 2201.8.0 + */ +public class FloatOps extends CommonOps implements BasicTypeOps { + @Override + public SubtypeData union(SubtypeData t1, SubtypeData t2) { + ArrayList values = new ArrayList<>(); + boolean allowed = EnumerableSubtype.enumerableSubtypeUnion((FloatSubtype) t1, (FloatSubtype) t2, values); + EnumerableFloat[] valueArray = new EnumerableFloat[values.size()]; + return FloatSubtype.createFloatSubtype(allowed, values.toArray(valueArray)); + } + + @Override + public SubtypeData intersect(SubtypeData t1, SubtypeData t2) { + ArrayList values = new ArrayList<>(); + boolean allowed = EnumerableSubtype.enumerableSubtypeIntersect((FloatSubtype) t1, (FloatSubtype) t2, values); + return FloatSubtype.createFloatSubtype(allowed, values.toArray(new EnumerableFloat[]{})); + } + + @Override + public SubtypeData diff(SubtypeData t1, SubtypeData t2) { + return intersect(t1, complement(t2)); + } + + @Override + public SubtypeData complement(SubtypeData t) { + FloatSubtype s = (FloatSubtype) t; + return FloatSubtype.createFloatSubtype(!s.allowed, s.values); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return Common.notIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/FunctionOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/FunctionOps.java new file mode 100644 index 000000000000..11ce0296a305 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/FunctionOps.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Common; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.FunctionAtomicType; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; + +import java.io.PrintStream; + +import static io.ballerina.types.Common.memoSubtypeIsEmpty; + +/** + * Function specific methods operate on SubtypeData. + * + * @since 2201.8.0 + */ +public class FunctionOps extends CommonOps implements BasicTypeOps { + + private static final PrintStream console = System.out; + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return memoSubtypeIsEmpty(cx, cx.functionMemo, + (context, bdd) -> Common.bddEvery(context, bdd, null, null, FunctionOps::functionFormulaIsEmpty), + (Bdd) t); + } + + private static boolean functionFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + return functionPathIsEmpty(cx, functionIntersectRet(cx, pos), functionUnionParams(cx, pos), + functionUnionQualifiers(cx, pos), pos, neg); + } + + private static boolean functionPathIsEmpty(Context cx, SemType rets, SemType params, SemType qualifiers, + Conjunction pos, Conjunction neg) { + if (neg == null) { + return false; + } else { + FunctionAtomicType t = cx.functionAtomType(neg.atom); + SemType t0 = t.paramType(); + SemType t1 = t.retType(); + SemType t2 = t.qualifiers(); + if (t.isGeneric()) { + // TODO: this is not correct. Ideally we should either resolve generic function calls to concrete + // function or properly support generics. However in order to support former we need some sort of + // monomorphization (at least for types) since generics can contain calls to other generics, and + // in order to do latter we need to define generics properly in spec. Untill that we are going to use + // a hack just to make sure internal libraries type checks passes + return (Core.isSubtype(cx, qualifiers, t2) && Core.isSubtype(cx, params, t0) && + Core.isSubtype(cx, rets, t1)) + || functionPathIsEmpty(cx, rets, params, qualifiers, pos, neg.next); + } + return (Core.isSubtype(cx, qualifiers, t2) && Core.isSubtype(cx, t0, params) && + functionPhi(cx, t0, Core.complement(t1), pos)) + || functionPathIsEmpty(cx, rets, params, qualifiers, pos, neg.next); + } + } + + private static boolean functionPhi(Context cx, SemType t0, SemType t1, Conjunction pos) { + if (pos == null) { + // t0 is NEVER only for function top types with qualifiers + return !Core.isNever(t0) && (Core.isEmpty(cx, t0) || Core.isEmpty(cx, t1)); + } + return functionPhiInner(cx, t0, t1, pos); + } + + private static boolean functionPhiInner(Context cx, SemType t0, SemType t1, Conjunction pos) { + if (pos == null) { + return Core.isEmpty(cx, t0) || Core.isEmpty(cx, t1); + } else { + FunctionAtomicType s = cx.functionAtomType(pos.atom); + SemType s0 = s.paramType(); + SemType s1 = s.retType(); + return (Core.isSubtype(cx, t0, s0) + || Core.isSubtype(cx, functionIntersectRet(cx, pos.next), Core.complement(t1))) + && functionPhiInner(cx, t0, Core.intersect(t1, s1), pos.next) + && functionPhiInner(cx, Core.diff(t0, s0), t1, pos.next); + } + } + + + private static SemType functionUnionParams(Context cx, Conjunction pos) { + if (pos == null) { + return PredefinedType.NEVER; + } + return Core.union(cx.functionAtomType(pos.atom).paramType(), functionUnionParams(cx, pos.next)); + } + + private static SemType functionUnionQualifiers(Context cx, Conjunction pos) { + if (pos == null) { + return PredefinedType.NEVER; + } + return Core.union(cx.functionAtomType(pos.atom).qualifiers(), functionUnionQualifiers(cx, pos.next)); + } + + + private static SemType functionIntersectRet(Context cx, Conjunction pos) { + if (pos == null) { + return PredefinedType.VAL; + } + return Core.intersect(cx.functionAtomType(pos.atom).retType(), functionIntersectRet(cx, pos.next)); + } + + private boolean functionTheta(Context cx, SemType t0, SemType t1, Conjunction pos) { + if (pos == null) { + return Core.isEmpty(cx, t0) || Core.isEmpty(cx, t1); + } else { + // replaces the SemType[2] [s0, s1] in nballerina where s0 = paramType, s1 = retType + FunctionAtomicType s = cx.functionAtomType(pos.atom); + SemType s0 = s.paramType(); + SemType s1 = s.retType(); + return (Core.isSubtype(cx, t0, s0) || functionTheta(cx, Core.diff(s0, t0), s1, pos.next)) + && (Core.isSubtype(cx, t1, Core.complement(s1)) + || functionTheta(cx, s0, Core.intersect(s1, t1), pos.next)); + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/FutureOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/FutureOps.java new file mode 100644 index 000000000000..ad4114f41706 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/FutureOps.java @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.typeops.MappingOps.mappingSubtypeIsEmpty; + +/** + * Basic type ops for future type. + * + * @since 2201.10.0 + */ +public class FutureOps extends CommonOps implements BasicTypeOps { + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return mappingSubtypeIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/IntOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/IntOps.java new file mode 100644 index 000000000000..accaf8640732 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/IntOps.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Common; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.RangeUnion; + +import java.util.ArrayList; +import java.util.List; + +import static java.lang.Long.MAX_VALUE; +import static java.lang.Long.MIN_VALUE; + +/** + * Basic subtype ops for int type. + * + * @since 2201.8.0 + */ +public class IntOps implements BasicTypeOps { + + private static IntOps intOpsInstance = new IntOps(); + + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + IntSubtype v1 = (IntSubtype) d1; + IntSubtype v2 = (IntSubtype) d2; + Range[] v = rangeListUnion(v1.ranges, v2.ranges); + if (v.length == 1 && v[0].min == MIN_VALUE && v[0].max == MAX_VALUE) { + return AllOrNothingSubtype.createAll(); + } + return IntSubtype.createIntSubtype(v); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + IntSubtype v1 = (IntSubtype) d1; + IntSubtype v2 = (IntSubtype) d2; + Range[] v = rangeListIntersect(v1.ranges, v2.ranges); + if (v.length == 0) { + return AllOrNothingSubtype.createNothing(); + } + return IntSubtype.createIntSubtype(v); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + IntSubtype v1 = (IntSubtype) d1; + IntSubtype v2 = (IntSubtype) d2; + Range[] v = rangeListIntersect(v1.ranges, rangeListComplement(v2.ranges)); + if (v.length == 0) { + return AllOrNothingSubtype.createNothing(); + } + return IntSubtype.createIntSubtype(v); + } + + @Override + public SubtypeData complement(SubtypeData d) { + IntSubtype v = (IntSubtype) d; + return IntSubtype.createIntSubtype(rangeListComplement(v.ranges)); + } + + static boolean intSubtypeOverlapRange(IntSubtype subtype, Range range) { + SubtypeData subtypeData = intOpsInstance.intersect(subtype, IntSubtype.createIntSubtype(range)); + return !(subtypeData instanceof AllOrNothingSubtype allOrNothingSubtype && + allOrNothingSubtype.isNothingSubtype()); + } + + public static long intSubtypeMax(IntSubtype subtype) { + return subtype.ranges[subtype.ranges.length - 1].max; + } + + public static long intSubtypeMin(IntSubtype subtype) { + return subtype.ranges[0].min; + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return Common.notIsEmpty(cx, t); + } + + private Range[] rangeListUnion(Range[] v1, Range[] v2) { + List result = new ArrayList<>(); + int i1 = 0; + int i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + + while (true) { + if (i1 >= len1) { + if (i2 >= len2) { + break; + } + rangeUnionPush(result, v2[i2]); + i2 += 1; + } else if (i2 >= len2) { + rangeUnionPush(result, v1[i1]); + i1 += 1; + } else { + Range r1 = v1[i1]; + Range r2 = v2[i2]; + RangeUnion combined = rangeUnion(r1, r2); + if (combined.status == 0) { + rangeUnionPush(result, combined.range); + i1 += 1; + i2 += 1; + } else if (combined.status < 0) { + rangeUnionPush(result, r1); + i1 += 1; + } else { + rangeUnionPush(result, r2); + i2 += 1; + } + } + } + Range[] rangeList = new Range[result.size()]; + return result.toArray(rangeList); + } + + private void rangeUnionPush(List ranges, Range next) { + int lastIndex = ranges.size() - 1; + if (lastIndex < 0) { + ranges.add(next); + return; + } + RangeUnion combined = rangeUnion(ranges.get(lastIndex), next); + if (combined.status == 0) { + ranges.set(lastIndex, combined.range); + } else { + ranges.add(next); + } + } + /* [from nballerina] Returns a range if there is a single range representing the union of r1 and r1. + -1 means union is empty because r1 is before r2, with no overlap + 1 means union is empty because r2 is before r1 with no overlap + Precondition r1 and r2 are non-empty */ + private RangeUnion rangeUnion(Range r1, Range r2) { + if (r1.max < r2.min) { + if (r1.max + 1 != r2.min) { + return RangeUnion.from(-1); + } + } + if (r2.max < r1.min) { + if (r2.max + 1 != r1.min) { + return RangeUnion.from(1); + } + } + return RangeUnion.from(new Range(Long.min(r1.min, r2.min), Long.max(r1.max, r2.max))); + } + + private Range[] rangeListIntersect(Range[] v1, Range[] v2) { + List result = new ArrayList<>(); + int i1 = 0; + int i2 = 0; + int len1 = v1.length; + int len2 = v2.length; + while (true) { + if (i1 >= len1 || i2 >= len2) { + break; + } else { + Range r1 = v1[i1]; + Range r2 = v2[i2]; + RangeUnion combined = rangeIntersect(r1, r2); + if (combined.status == 0) { + result.add(combined.range); + i1 += 1; + i2 += 1; + } else if (combined.status < 0) { + i1 += 1; + } else { + i2 += 1; + } + } + } + Range[] rangeList = new Range[result.size()]; + return result.toArray(rangeList); + } + + /* [from nballerina] When Range is returned, it is non-empty and the intersection of r1 and r2 + -1 means empty intersection because r1 before r2 + 1 means empty intersection because r1 after r2 */ + private RangeUnion rangeIntersect(Range r1, Range r2) { + if (r1.max < r2.min) { + return RangeUnion.from(-1); + } + if (r2.max < r1.min) { + return RangeUnion.from(1); + } + return RangeUnion.from(new Range(Long.max(r1.min, r2.min), Long.min(r1.max, r2.max))); + } + + private Range[] rangeListComplement(Range[] v) { + List result = new ArrayList<>(); + int len = v.length; + long min = v[0].min; + if (min > MIN_VALUE) { + result.add(new Range(MIN_VALUE, min - 1)); + } + for (int i = 1; i < len; i++) { + result.add(new Range(v[i - 1].max + 1, v[i].min - 1)); + } + long max = v[v.length - 1].max; + if (max < MAX_VALUE) { + result.add(new Range(max + 1, MAX_VALUE)); + } + return result.toArray(new Range[]{}); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/ListOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/ListOps.java new file mode 100644 index 000000000000..406bd48e91e3 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/ListOps.java @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellSemType; +import io.ballerina.types.Common; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.IntStream; + +import static io.ballerina.types.Common.bddSubtypeComplement; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.Common.bddSubtypeIntersect; +import static io.ballerina.types.Common.bddSubtypeUnion; +import static io.ballerina.types.Common.memoSubtypeIsEmpty; +import static io.ballerina.types.Core.cellContainingInnerVal; +import static io.ballerina.types.Core.cellInner; +import static io.ballerina.types.Core.cellInnerVal; +import static io.ballerina.types.Core.intersectMemberSemTypes; +import static io.ballerina.types.PredefinedType.LIST_ATOMIC_INNER; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.subtypedata.IntSubtype.intSubtypeContains; +import static io.ballerina.types.typeops.IntOps.intSubtypeMax; +import static io.ballerina.types.typeops.IntOps.intSubtypeOverlapRange; + +/** + * Basic type ops for list type. + * + * @since 2201.8.0 + */ +public class ListOps extends CommonOps implements BasicTypeOps { + + static boolean listSubtypeIsEmpty(Context cx, SubtypeData t) { + return memoSubtypeIsEmpty(cx, cx.listMemo, + (context, bdd) -> Common.bddEvery(context, bdd, null, null, ListOps::listFormulaIsEmpty), + (Bdd) t); + } + + private static boolean listFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + FixedLengthArray members; + CellSemType rest; + if (pos == null) { + ListAtomicType atom = LIST_ATOMIC_INNER; + members = atom.members(); + rest = atom.rest(); + } else { + // combine all the positive tuples using intersection + ListAtomicType lt = cx.listAtomType(pos.atom); + members = lt.members(); + rest = lt.rest(); + Conjunction p = pos.next; + // the neg case is in case we grow the array in listInhabited + if (p != null || neg != null) { + // Jbal note: we don't need this as we already created copies when converting from array to list. + // Just keeping this for the sake of source similarity between Bal code and Java. + members = fixedArrayShallowCopy(members); + } + while (true) { + if (p == null) { + break; + } else { + Atom d = p.atom; + p = p.next; + lt = cx.listAtomType(d); + TwoTuple + intersected = listIntersectWith(cx.env, members, rest, lt.members(), lt.rest()); + if (intersected == null) { + return true; + } + members = intersected.item1; + rest = intersected.item2; + } + } + if (fixedArrayAnyEmpty(cx, members)) { + return true; + } + } + List indices = listSamples(cx, members, rest, neg); + TwoTuple, Integer> sampleTypes = listSampleTypes(cx, members, rest, indices); + return !listInhabited(cx, indices.toArray(new Integer[0]), + sampleTypes.item1.toArray(SemType[]::new), + sampleTypes.item2, neg); + } + + public static TwoTuple, Integer> listSampleTypes(Context cx, FixedLengthArray members, + CellSemType rest, List indices) { + List memberTypes = new ArrayList<>(); + int nRequired = 0; + for (int i = 0; i < indices.size(); i++) { + int index = indices.get(i); + CellSemType t = cellContainingInnerVal(cx.env, listMemberAt(members, rest, index)); + if (Core.isEmpty(cx, t)) { + break; + } + memberTypes.add(t); + if (index < members.fixedLength()) { + nRequired = i + 1; + } + } + return TwoTuple.from(memberTypes, nRequired); + } + + // Return a list of sample indices for use as second argument of `listInhabited`. + // The positive list type P is represented by `members` and `rest`. + // The negative list types N are represented by `neg` + // The `indices` list (first member of return value) is constructed in two stages. + // First, the set of all non-negative integers is partitioned so that two integers are + // in different partitions if they get different types as an index in P or N. + // Second, we choose a number of samples from each partition. It doesn't matter + // which sample we choose, but (this is the key point) we need at least as many samples + // as there are negatives in N, so that for each negative we can freely choose a type for the sample + // to avoid being matched by that negative. + static List listSamples(Context cx, FixedLengthArray members, SemType rest, Conjunction neg) { + int maxInitialLength = members.initial().size(); + List fixedLengths = new ArrayList<>(); + fixedLengths.add(members.fixedLength()); + Conjunction tem = neg; + int nNeg = 0; + while (true) { + if (tem != null) { + ListAtomicType lt = cx.listAtomType(tem.atom); + FixedLengthArray m = lt.members(); + maxInitialLength = Integer.max(maxInitialLength, m.initial().size()); + if (m.fixedLength() > maxInitialLength) { + fixedLengths.add(m.fixedLength()); + } + nNeg += 1; + tem = tem.next; + } else { + break; + } + } + Collections.sort(fixedLengths); + // `boundaries` partitions the non-negative integers + // Construct `boundaries` from `fixedLengths` and `maxInitialLength` + // An index b is a boundary point if indices < b are different from indices >= b + //int[] boundaries = from int i in 1 ... maxInitialLength select i; + List boundaries = new ArrayList<>(); + for (int i = 1; i <= maxInitialLength; i++) { + boundaries.add(i); + } + for (int n : fixedLengths) { + // this also removes duplicates + if (boundaries.size() == 0 || n > boundaries.get(boundaries.size() - 1)) { + boundaries.add(n); + } + } + // Now construct the list of indices by taking nNeg samples from each partition. + List indices = new ArrayList<>(); + int lastBoundary = 0; + if (nNeg == 0) { + // this is needed for when this is used in listProj + nNeg = 1; + } + for (int b : boundaries) { + int segmentLength = b - lastBoundary; + // Cannot have more samples than are in the parition. + int nSamples = Integer.min(segmentLength, nNeg); + for (int i = b - nSamples; i < b; i++) { + indices.add(i); + } + lastBoundary = b; + } + for (int i = 0; i < nNeg; i++) { + // Be careful to avoid integer overflow. + if (lastBoundary > Integer.MAX_VALUE - i) { + break; + } + indices.add(lastBoundary + i); + } + return indices; + } + + static TwoTuple listIntersectWith(Env env, FixedLengthArray members1, + CellSemType rest1, FixedLengthArray members2, + CellSemType rest2) { + if (listLengthsDisjoint(members1, rest1, members2, rest2)) { + return null; + } + // This is different from nBallerina, but I think assuming we have normalized the FixedLengthArrays we must + // consider fixedLengths not the size of initial members. For example consider any[4] and + // [int, string, float...]. If we don't consider the fixedLength in the initial part we'll consider only the + // first two elements and rest will compare essentially 5th element, meaning we are ignoring 3 and 4 elements + int max = Integer.max(members1.fixedLength(), members2.fixedLength()); + List initial = + IntStream.range(0, max) + .mapToObj(i -> intersectMemberSemTypes(env, listMemberAt(members1, rest1, i), + listMemberAt(members2, rest2, i))) + .toList(); + return TwoTuple.from(FixedLengthArray.from(initial, + Integer.max(members1.fixedLength(), members2.fixedLength())), + intersectMemberSemTypes(env, rest1, rest2)); + } + + static FixedLengthArray fixedArrayShallowCopy(FixedLengthArray array) { + return FixedLengthArray.from(array.initial(), array.fixedLength()); + } + + // This function determines whether a list type P & N is inhabited. + // where P is a positive list type and N is a list of negative list types. + // With just straightforward fixed-length tuples we can consider every index of the tuple. + // But we cannot do this in general because of rest types and fixed length array types e.g. `byte[10000000]`. + // So we consider instead a collection of indices that is sufficient for us to determine inhabitation, + // given the types of P and N. + // `indices` is this list of sample indices: these are indicies into members of the list type. + // We don't represent P directly. Instead P is represented by `memberTypes` and `nRequired`: + // `memberTypes[i]` is the type that P gives to `indices[i]`; + // `nRequired` is the number of members of `memberTypes` that are required by P. + // `neg` represents N. + static boolean listInhabited(Context cx, Integer[] indices, SemType[] memberTypes, int nRequired, Conjunction neg) { + // TODO: Way `listInhabited` method works is essentially removing negative atoms one by one until either we + // run-out of negative atoms or there is nothing left in the positive (intersection) atom. While correctness + // of this method is independent of the order of negative atoms, the performance of this method is dependent + // on the order. For example consider positive atom is int[] and we have negative atoms int[1], int[2], + // int[3], any[]. If we start with any[] we immediately know it is not inhabited whereas in any other order we + // have to evaluate until we come to any[] to figure this out. We say any[] is larger than others since it + // covers more values than them. According to Frisch section 7.3.1 we should use this size as an heuristic to + // sort negative atoms. However it is not clear to me how to estimate the size correctly + if (neg == null) { + return true; + } else { + final ListAtomicType nt = cx.listAtomType(neg.atom); + if (nRequired > 0 && Core.isNever(listMemberAtInnerVal(nt.members(), nt.rest(), indices[nRequired - 1]))) { + // Skip this negative if it is always shorter than the minimum required by the positive + return listInhabited(cx, indices, memberTypes, nRequired, neg.next); + } + int negLen = nt.members().fixedLength(); + if (negLen > 0) { + // If we have isEmpty(T1 & S1) or isEmpty(T2 & S2) then we have [T1, T2] / [S1, S2] = [T1, T2]. + // Therefore, we can skip the negative + for (int i = 0; i < memberTypes.length; i++) { + int index = indices[i]; + if (index >= negLen) { + break; + } + SemType negMemberType = listMemberAt(nt.members(), nt.rest(), index); + SemType common = Core.intersect(memberTypes[i], negMemberType); + if (Core.isEmpty(cx, common)) { + return listInhabited(cx, indices, memberTypes, nRequired, neg.next); + } + } + // Consider cases we can avoid this negative by having a sufficiently short list + int len = memberTypes.length; + if (len < indices.length && indices[len] < negLen) { + return listInhabited(cx, indices, memberTypes, nRequired, neg.next); + } + for (int i = nRequired; i < memberTypes.length; i++) { + if (indices[i] >= negLen) { + break; + } + // TODO: avoid creating new arrays here + SemType[] t = Arrays.copyOfRange(memberTypes, 0, i); + if (listInhabited(cx, indices, t, nRequired, neg.next)) { + return true; + } + } + } + // Now we need to explore the possibility of shapes with length >= neglen + // This is the heart of the algorithm. + // For [v0, v1] not to be in [t0,t1], there are two possibilities + // (1) v0 is not in t0, or + // (2) v1 is not in t1 + // Case (1) + // For v0 to be in s0 but not t0, d0 must not be empty. + // We must then find a [v0,v1] satisfying the remaining negated tuples, + // such that v0 is in d0. + // SemType d0 = diff(s[0], t[0]); + // if !isEmpty(cx, d0) && tupleInhabited(cx, [d0, s[1]], neg.rest) { + // return true; + // } + // Case (2) + // For v1 to be in s1 but not t1, d1 must not be empty. + // We must then find a [v0,v1] satisfying the remaining negated tuples, + // such that v1 is in d1. + // SemType d1 = diff(s[1], t[1]); + // return !isEmpty(cx, d1) && tupleInhabited(cx, [s[0], d1], neg.rest); + // We can generalize this to tuples of arbitrary length. + for (int i = 0; i < memberTypes.length; i++) { + SemType d = Core.diff(memberTypes[i], listMemberAt(nt.members(), nt.rest(), indices[i])); + if (!Core.isEmpty(cx, d)) { + SemType[] t = memberTypes.clone(); + t[i] = d; + // We need to make index i be required + if (listInhabited(cx, indices, t, Integer.max(nRequired, i + 1), neg.next)) { + return true; + } + } + } + // This is correct for length 0, because we know that the length of the + // negative is 0, and [] - [] is empty. + return false; + } + } + + static SemType listMemberAtInnerVal(FixedLengthArray fixedArray, CellSemType rest, int index) { + return cellInnerVal(listMemberAt(fixedArray, rest, index)); + } + + private static boolean listLengthsDisjoint(FixedLengthArray members1, CellSemType rest1, + FixedLengthArray members2, CellSemType rest2) { + int len1 = members1.fixedLength(); + int len2 = members2.fixedLength(); + if (len1 < len2) { + return Core.isNever(cellInnerVal(rest1)); + } + if (len2 < len1) { + return Core.isNever(cellInnerVal(rest2)); + } + return false; + } + + static CellSemType listMemberAt(FixedLengthArray fixedArray, CellSemType rest, int index) { + if (index < fixedArray.fixedLength()) { + return fixedArrayGet(fixedArray, index); + } + return rest; + } + + static boolean fixedArrayAnyEmpty(Context cx, FixedLengthArray array) { + for (var t : array.initial()) { + if (Core.isEmpty(cx, t)) { + return true; + } + } + return false; + } + + private static CellSemType fixedArrayGet(FixedLengthArray members, int index) { + int memberLen = members.initial().size(); + int i = Integer.min(index, memberLen - 1); + return members.initial().get(i); + } + + static SemType listAtomicMemberTypeInnerVal(ListAtomicType atomic, SubtypeData key) { + return Core.diff(listAtomicMemberTypeInner(atomic, key), UNDEF); + } + + private static SemType listAtomicMemberTypeInner(ListAtomicType atomic, SubtypeData key) { + return listAtomicMemberTypeAtInner(atomic.members(), atomic.rest(), key); + } + + static SemType listAtomicMemberTypeAtInner(FixedLengthArray fixedArray, CellSemType rest, SubtypeData key) { + if (key instanceof IntSubtype intSubtype) { + SemType m = NEVER; + int initLen = fixedArray.initial().size(); + int fixedLen = fixedArray.fixedLength(); + if (fixedLen != 0) { + for (int i = 0; i < initLen; i++) { + if (intSubtypeContains(key, i)) { + m = Core.union(m, cellInner(fixedArrayGet(fixedArray, i))); + } + } + if (intSubtypeOverlapRange(intSubtype, Range.from(initLen, fixedLen - 1))) { + m = Core.union(m, cellInner(fixedArrayGet(fixedArray, fixedLen - 1))); + } + } + if (fixedLen == 0 || intSubtypeMax((IntSubtype) key) > fixedLen - 1) { + m = Core.union(m, cellInner(rest)); + } + return m; + } + SemType m = cellInner(rest); + if (fixedArray.fixedLength() > 0) { + for (CellSemType ty : fixedArray.initial()) { + m = Core.union(m, cellInner(ty)); + } + } + return m; + } + + public static SemType bddListMemberTypeInnerVal(Context cx, Bdd b, SubtypeData key, SemType accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : NEVER; + } else { + BddNode bddNode = (BddNode) b; + return Core.union(bddListMemberTypeInnerVal(cx, bddNode.left(), key, + Core.intersect(listAtomicMemberTypeInnerVal(cx.listAtomType(bddNode.atom()), key), accum)), + Core.union(bddListMemberTypeInnerVal(cx, bddNode.middle(), key, accum), + bddListMemberTypeInnerVal(cx, bddNode.right(), key, accum))); + } + } + + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + return bddSubtypeUnion(d1, d2); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + return bddSubtypeIntersect(d1, d2); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + return bddSubtypeDiff(d1, d2); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return bddSubtypeComplement(d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData d) { + return listSubtypeIsEmpty(cx, d); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/ListProj.java b/semtypes/src/main/java/io/ballerina/types/typeops/ListProj.java new file mode 100644 index 000000000000..379da6baeae0 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/ListProj.java @@ -0,0 +1,236 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.Atom; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellSemType; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.FixedLengthArray; +import io.ballerina.types.ListAtomicType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.CellSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; + +import static io.ballerina.types.BasicTypeCode.BT_LIST; +import static io.ballerina.types.Common.isNothingSubtype; +import static io.ballerina.types.Conjunction.and; +import static io.ballerina.types.Core.cellInnerVal; +import static io.ballerina.types.Core.diff; +import static io.ballerina.types.Core.getComplexSubtypeData; +import static io.ballerina.types.Core.isEmpty; +import static io.ballerina.types.Core.isNever; +import static io.ballerina.types.Core.union; +import static io.ballerina.types.PredefinedType.LIST; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.PredefinedType.VAL; +import static io.ballerina.types.subtypedata.CellSubtype.cellContaining; +import static io.ballerina.types.subtypedata.IntSubtype.intSubtypeContains; +import static io.ballerina.types.typeops.ListOps.fixedArrayAnyEmpty; +import static io.ballerina.types.typeops.ListOps.fixedArrayShallowCopy; +import static io.ballerina.types.typeops.ListOps.listIntersectWith; +import static io.ballerina.types.typeops.ListOps.listMemberAtInnerVal; + +/** + * Class to hold functions ported from `listProj.bal` file. + * + * @since 2201.8.0 + */ +public class ListProj { + // Untested full implementation of list projection. + + // Based on listMemberType + public static SemType listProjInnerVal(Context cx, SemType t, SemType k) { + if (t instanceof BasicTypeBitSet b) { + return (b.bitset & LIST.bitset) != 0 ? VAL : NEVER; + } else { + SubtypeData keyData = Core.intSubtype(k); + if (isNothingSubtype(keyData)) { + return NEVER; + } + return listProjBddInnerVal(cx, keyData, (Bdd) getComplexSubtypeData((ComplexSemType) t, BT_LIST), null, + null); + } + } + + // Based on bddEvery + static SemType listProjBddInnerVal(Context cx, SubtypeData k, Bdd b, Conjunction pos, Conjunction neg) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? listProjPathInnerVal(cx, k, pos, neg) : NEVER; + } else { + BddNode bddNode = (BddNode) b; + return union(listProjBddInnerVal(cx, k, bddNode.left(), and(bddNode.atom(), pos), neg), + union(listProjBddInnerVal(cx, k, bddNode.middle(), pos, neg), + listProjBddInnerVal(cx, k, bddNode.right(), pos, and(bddNode.atom(), neg)))); + } + } + + // Based on listFormulaIsEmpty + static SemType listProjPathInnerVal(Context cx, SubtypeData k, Conjunction pos, Conjunction neg) { + FixedLengthArray members; + CellSemType rest; + if (pos == null) { + members = FixedLengthArray.empty(); + rest = cellContaining(cx.env, union(VAL, UNDEF)); + } else { + // combine all the positive tuples using intersection + ListAtomicType lt = cx.listAtomType(pos.atom); + members = lt.members(); + rest = lt.rest(); + Conjunction p = pos.next; + // the neg case is in case we grow the array in listInhabited + if (p != null || neg != null) { + members = fixedArrayShallowCopy(members); + } + + while (true) { + if (p == null) { + break; + } else { + Atom d = p.atom; + p = p.next; + lt = cx.listAtomType(d); + TwoTuple + intersected = listIntersectWith(cx.env, members, rest, lt.members(), lt.rest()); + if (intersected == null) { + return NEVER; + } + members = intersected.item1; + rest = intersected.item2; + } + } + if (fixedArrayAnyEmpty(cx, members)) { + return NEVER; + } + // Ensure that we can use isNever on rest in listInhabited + if (!Core.isNever(cellInnerVal(rest)) && isEmpty(cx, rest)) { + rest = CellSubtype.roCellContaining(cx.env, NEVER); + } + } + // return listProjExclude(cx, k, members, rest, listConjunction(cx, neg)); + List indices = ListOps.listSamples(cx, members, rest, neg); + TwoTuple, List> projSamples = listProjSamples(indices, k); + indices = projSamples.item1; + TwoTuple, Integer> sampleTypes = ListOps.listSampleTypes(cx, members, rest, indices); + return listProjExcludeInnerVal(cx, projSamples.item1.toArray(Integer[]::new), + projSamples.item2.toArray(Integer[]::new), + sampleTypes.item1.toArray(CellSemType[]::new), + sampleTypes.item2, neg); + } + + // In order to adapt listInhabited to do projection, we need + // to know which samples correspond to keys and to ensure that + // every equivalence class that overlaps with a key has a sample in the + // intersection. + // Here we add samples for both ends of each range. This doesn't handle the + // case where the key is properly within a partition: but that is handled + // because we already have a sample of the end of the partition. + private static TwoTuple, List> listProjSamples(List indices, SubtypeData k) { + List> v = new ArrayList<>(); + for (int i : indices) { + v.add(TwoTuple.from(i, intSubtypeContains(k, i))); + } + if (k instanceof IntSubtype intSubtype) { + for (Range range : intSubtype.ranges) { + long max = range.max; + if (range.max >= 0) { + v.add(TwoTuple.from((int) max, true)); + int min = Integer.max(0, (int) range.min); + if (min < max) { + v.add(TwoTuple.from(min, true)); + } + } + } + } + v.sort(Comparator.comparingInt(p -> p.item1)); + List indices1 = new ArrayList<>(); + List keyIndices = new ArrayList<>(); + for (var ib : v) { + if (indices1.isEmpty() || !Objects.equals(ib.item1, indices1.get(indices1.size() - 1))) { + if (ib.item2) { + keyIndices.add(indices1.size()); + } + indices1.add(ib.item1); + } + } + return TwoTuple.from(indices1, keyIndices); + } + + // `keyIndices` are the indices in `memberTypes` of those samples that belong to the key type. + // Based on listInhabited + // Corresponds to phi^x in AMK tutorial generalized for list types. + static SemType listProjExcludeInnerVal(Context cx, Integer[] indices, Integer[] keyIndices, + CellSemType[] memberTypes, int nRequired, Conjunction neg) { + SemType p = NEVER; + if (neg == null) { + int len = memberTypes.length; + for (int k : keyIndices) { + if (k < len) { + p = union(p, cellInnerVal(memberTypes[k])); + } + } + } else { + final ListAtomicType nt = cx.listAtomType(neg.atom); + if (nRequired > 0 && isNever(listMemberAtInnerVal(nt.members(), nt.rest(), indices[nRequired - 1]))) { + return listProjExcludeInnerVal(cx, indices, keyIndices, memberTypes, nRequired, neg.next); + } + int negLen = nt.members().fixedLength(); + if (negLen > 0) { + int len = memberTypes.length; + if (len < indices.length && indices[len] < negLen) { + return listProjExcludeInnerVal(cx, indices, keyIndices, memberTypes, nRequired, neg.next); + } + for (int i = nRequired; i < memberTypes.length; i++) { + if (indices[i] >= negLen) { + break; + } + // TODO: think about a way to avoid this allocation here and instead create view and pass it in + CellSemType[] t = Arrays.copyOfRange(memberTypes, 0, i); + p = union(p, listProjExcludeInnerVal(cx, indices, keyIndices, t, nRequired, neg.next)); + } + } + for (int i = 0; i < memberTypes.length; i++) { + SemType d = + diff(cellInnerVal(memberTypes[i]), listMemberAtInnerVal(nt.members(), nt.rest(), indices[i])); + if (!Core.isEmpty(cx, d)) { + CellSemType[] t = memberTypes.clone(); + t[i] = cellContaining(cx.env, d); + // We need to make index i be required + p = union(p, listProjExcludeInnerVal(cx, indices, keyIndices, t, Integer.max(nRequired, i + 1), + neg.next)); + } + } + } + return p; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/MappingOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/MappingOps.java new file mode 100644 index 000000000000..17305851e6cd --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/MappingOps.java @@ -0,0 +1,243 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.CellSemType; +import io.ballerina.types.Common; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.Env; +import io.ballerina.types.MappingAtomicType; +import io.ballerina.types.SemType; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.BddNode; +import io.ballerina.types.subtypedata.StringSubtype; + +import java.util.ArrayList; +import java.util.List; + +import static io.ballerina.types.Common.bddSubtypeComplement; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.Common.bddSubtypeIntersect; +import static io.ballerina.types.Common.bddSubtypeUnion; +import static io.ballerina.types.Common.isAllSubtype; +import static io.ballerina.types.Common.memoSubtypeIsEmpty; +import static io.ballerina.types.PredefinedType.MAPPING_ATOMIC_INNER; +import static io.ballerina.types.PredefinedType.NEVER; +import static io.ballerina.types.PredefinedType.UNDEF; +import static io.ballerina.types.typeops.StringOps.stringSubtypeListCoverage; + +/** + * Basic type ops for mapping type. + * + * @since 2201.8.0 + */ +public class MappingOps extends CommonOps implements BasicTypeOps { + // This works the same as the tuple case, except that instead of + // just comparing the lengths of the tuples we compare the sorted list of field names + public static boolean mappingFormulaIsEmpty(Context cx, Conjunction posList, Conjunction negList) { + MappingAtomicType combined; + if (posList == null) { + combined = MAPPING_ATOMIC_INNER; + } else { + // combine all the positive atoms using intersection + combined = cx.mappingAtomType(posList.atom); + Conjunction p = posList.next; + while (true) { + if (p == null) { + break; + } else { + MappingAtomicType m = intersectMapping(cx.env, combined, cx.mappingAtomType(p.atom)); + if (m == null) { + return true; + } else { + combined = m; + } + p = p.next; + } + } + for (SemType t : combined.types()) { + if (Core.isEmpty(cx, t)) { + return true; + } + } + + } + return !mappingInhabited(cx, combined, negList); + } + + private static boolean mappingInhabited(Context cx, MappingAtomicType pos, Conjunction negList) { + if (negList == null) { + return true; + } else { + MappingAtomicType neg = cx.mappingAtomType(negList.atom); + + FieldPairs pairing = new FieldPairs(pos, neg); + if (!Core.isEmpty(cx, Core.diff(pos.rest(), neg.rest()))) { + return mappingInhabited(cx, pos, negList.next); + } + for (FieldPair fieldPair : pairing) { + SemType intersect = Core.intersect(fieldPair.type1(), fieldPair.type2()); + // if types of at least one field are disjoint, the neg atom will not contribute to the next iteration. + // Therefore, we can skip the current neg atom. + // i.e. if we have isEmpty(T1 & S1) or isEmpty(T2 & S2) then, + // record { T1 f1; T2 f2; } / record { S1 f1; S2 f2; } = record { T1 f1; T2 f2; } + if (Core.isEmpty(cx, intersect)) { + return mappingInhabited(cx, pos, negList.next); + } + + CellSemType d = (CellSemType) Core.diff(fieldPair.type1(), fieldPair.type2()); + if (!Core.isEmpty(cx, d)) { + MappingAtomicType mt; + if (fieldPair.index1() == null) { + // the posType came from the rest type + mt = insertField(pos, fieldPair.name(), d); + } else { + CellSemType[] posTypes = pos.types(); + posTypes[fieldPair.index1()] = d; + mt = MappingAtomicType.from(pos.names(), posTypes, pos.rest()); + } + if (mappingInhabited(cx, mt, negList.next)) { + return true; + } + } + } + return false; + } + } + + private static MappingAtomicType insertField(MappingAtomicType m, String name, CellSemType t) { + String[] orgNames = m.names(); + String[] names = Common.shallowCopyStrings(orgNames, orgNames.length + 1); + CellSemType[] orgTypes = m.types(); + CellSemType[] types = Common.shallowCopyCellTypes(orgTypes, orgTypes.length + 1); + int i = orgNames.length; + while (true) { + if (i == 0 || Common.codePointCompare(names[i - 1], name)) { + names[i] = name; + types[i] = t; + break; + } + names[i] = names[i - 1]; + types[i] = types[i - 1]; + i -= 1; + } + return MappingAtomicType.from(names, types, m.rest()); + } + + private static MappingAtomicType intersectMapping(Env env, MappingAtomicType m1, MappingAtomicType m2) { + List names = new ArrayList<>(); + List types = new ArrayList<>(); + FieldPairs pairing = new FieldPairs(m1, m2); + for (FieldPair fieldPair : pairing) { + names.add(fieldPair.name()); + CellSemType t = Core.intersectMemberSemTypes(env, fieldPair.type1(), fieldPair.type2()); + if (Core.isNever(Core.cellInner(fieldPair.type1()))) { + return null; + } + types.add(t); + } + CellSemType rest = Core.intersectMemberSemTypes(env, m1.rest(), m2.rest()); + return MappingAtomicType.from(names.toArray(new String[]{}), types.toArray(new CellSemType[]{}), rest); + } + + public static boolean mappingSubtypeIsEmpty(Context cx, SubtypeData t) { + return memoSubtypeIsEmpty(cx, cx.mappingMemo, + (context, bdd) -> Common.bddEvery(context, bdd, null, null, MappingOps::mappingFormulaIsEmpty), + (Bdd) t); + } + + public static SemType bddMappingMemberTypeInner(Context cx, Bdd b, SubtypeData key, SemType accum) { + if (b instanceof BddAllOrNothing allOrNothing) { + return allOrNothing.isAll() ? accum : NEVER; + } else { + BddNode bdd = (BddNode) b; + return Core.union( + bddMappingMemberTypeInner(cx, bdd.left(), key, + Core.intersect(mappingAtomicMemberTypeInner(cx.mappingAtomType(bdd.atom()), key), + accum)), + Core.union(bddMappingMemberTypeInner(cx, bdd.middle(), key, accum), + bddMappingMemberTypeInner(cx, bdd.right(), key, accum))); + } + } + + static SemType mappingAtomicMemberTypeInner(MappingAtomicType atomic, SubtypeData key) { + SemType memberType = null; + for (SemType ty : mappingAtomicApplicableMemberTypesInner(atomic, key)) { + if (memberType == null) { + memberType = ty; + } else { + memberType = Core.union(memberType, ty); + } + } + return memberType == null ? UNDEF : memberType; + } + + static List mappingAtomicApplicableMemberTypesInner(MappingAtomicType atomic, SubtypeData key) { + List types = new ArrayList<>(atomic.types().length); + for (CellSemType t : atomic.types()) { + types.add(Core.cellInner(t)); + } + + List memberTypes = new ArrayList<>(); + SemType rest = Core.cellInner(atomic.rest()); + if (isAllSubtype(key)) { + memberTypes.addAll(types); + memberTypes.add(rest); + } else { + StringSubtype.StringSubtypeListCoverage coverage = stringSubtypeListCoverage((StringSubtype) key, + atomic.names()); + for (int index : coverage.indices) { + memberTypes.add(types.get(index)); + } + if (!coverage.isSubtype) { + memberTypes.add(rest); + } + } + return memberTypes; + } + + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + return bddSubtypeUnion(d1, d2); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + return bddSubtypeIntersect(d1, d2); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + return bddSubtypeDiff(d1, d2); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return bddSubtypeComplement(d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData d) { + return mappingSubtypeIsEmpty(cx, d); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/MappingPairIterator.java b/semtypes/src/main/java/io/ballerina/types/typeops/MappingPairIterator.java new file mode 100644 index 000000000000..f929daacf2d5 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/MappingPairIterator.java @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.CellSemType; +import io.ballerina.types.Common; +import io.ballerina.types.MappingAtomicType; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; + +/** + * Iteration implementation of `MappingPairIterator`. + * + * @since 2201.8.0 + */ +public class MappingPairIterator implements Iterator { + private final String[] names1; + private final String[] names2; + private final CellSemType[] types1; + private final CellSemType[] types2; + private final int len1; + private final int len2; + private int i1 = 0; + private int i2 = 0; + private final CellSemType rest1; + private final CellSemType rest2; + + private boolean doneIteration = false; + private boolean shouldCalculate = true; + private FieldPair cache = null; + + public MappingPairIterator(MappingAtomicType m1, MappingAtomicType m2) { + this.names1 = m1.names(); + this.len1 = this.names1.length; + this.types1 = m1.types(); + this.rest1 = m1.rest(); + this.names2 = m2.names(); + this.len2 = this.names2.length; + this.types2 = m2.types(); + this.rest2 = m2.rest(); + } + + @Override + public boolean hasNext() { + if (this.doneIteration) { + return false; + } + if (this.shouldCalculate) { + FieldPair cache = internalNext(); + if (cache == null) { + this.doneIteration = true; + } + this.cache = cache; + this.shouldCalculate = false; + } + return !this.doneIteration; + } + + @Override + public FieldPair next() { + if (this.doneIteration) { + throw new NoSuchElementException("Exhausted iterator"); + } + + if (this.shouldCalculate) { + FieldPair cache = internalNext(); + if (cache == null) { + // this.doneIteration = true; + throw new IllegalStateException(); + } + this.cache = cache; + } + this.shouldCalculate = true; + return this.cache; + } + + /* + * This method corresponds to `next` method of MappingPairing. + */ + private FieldPair internalNext() { + FieldPair p; + if (this.i1 >= this.len1) { + if (this.i2 >= this.len2) { + return null; + } + p = FieldPair.create(curName2(), this.rest1, curType2(), null, this.i2); + this.i2 += 1; + } else if (this.i2 >= this.len2) { + p = FieldPair.create(curName1(), curType1(), this.rest2, this.i1, null); + this.i1 += 1; + } else { + String name1 = curName1(); + String name2 = curName2(); + if (Common.codePointCompare(name1, name2)) { + p = FieldPair.create(name1, curType1(), this.rest2, this.i1, null); + this.i1 += 1; + } else if (Common.codePointCompare(name2, name1)) { + p = FieldPair.create(name2, this.rest1, curType2(), null, this.i2); + this.i2 += 1; + } else { + p = FieldPair.create(name1, curType1(), curType2(), this.i1, this.i2); + this.i1 += 1; + this.i2 += 1; + } + } + return p; + } + + private CellSemType curType1() { + return this.types1[this.i1]; + } + + private String curName1() { + return this.names1[this.i1]; + } + + private CellSemType curType2() { + return this.types2[this.i2]; + } + + private String curName2() { + return this.names2[this.i2]; + } + + public void reset() { + this.i1 = 0; + this.i2 = 0; + } + + public Optional index1(String name) { + int i1Prev = this.i1 - 1; + return i1Prev >= 0 && Objects.equals(this.names1[i1Prev], name) ? Optional.of(i1Prev) : Optional.empty(); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/ObjectOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/ObjectOps.java new file mode 100644 index 000000000000..47518f536d1b --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/ObjectOps.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.Common.bddEveryPositive; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.Common.memoSubtypeIsEmpty; +import static io.ballerina.types.PredefinedType.MAPPING_SUBTYPE_OBJECT; + +public final class ObjectOps extends CommonOps implements BasicTypeOps { + + @Override + public SubtypeData complement(SubtypeData t) { + return objectSubTypeComplement(t); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return objectSubTypeIsEmpty(cx, t); + } + + private static boolean objectSubTypeIsEmpty(Context cx, SubtypeData t) { + return memoSubtypeIsEmpty(cx, cx.mappingMemo, ObjectOps::objectBddIsEmpty, (Bdd) t); + } + + private static boolean objectBddIsEmpty(Context cx, Bdd b) { + return bddEveryPositive(cx, b, null, null, MappingOps::mappingFormulaIsEmpty); + } + + private SubtypeData objectSubTypeComplement(SubtypeData t) { + return bddSubtypeDiff(MAPPING_SUBTYPE_OBJECT, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/StreamOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/StreamOps.java new file mode 100644 index 000000000000..17948aaecc6c --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/StreamOps.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.Common.bddPosMaybeEmpty; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.PredefinedType.LIST_SUBTYPE_TWO_ELEMENT; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; +import static io.ballerina.types.typeops.ListOps.listSubtypeIsEmpty; + +/** + * Basic type ops for stream type. + * + * @since 2201.10.0 + */ +public class StreamOps extends CommonOps implements BasicTypeOps { + + private static SubtypeData streamSubtypeComplement(SubtypeData t) { + return bddSubtypeDiff(LIST_SUBTYPE_TWO_ELEMENT, t); + } + + private static boolean streamSubtypeIsEmpty(Context cx, SubtypeData t) { + Bdd b = (Bdd) t; + // The goal of this is to ensure that listSubtypeIsEmpty call beneath does + // not get an empty posList, because it will interpret that + // as `[any|error...]` rather than `[any|error, any|error]`. + b = bddPosMaybeEmpty(b) ? bddIntersect(b, LIST_SUBTYPE_TWO_ELEMENT) : b; + return listSubtypeIsEmpty(cx, b); + } + + @Override + public SubtypeData complement(SubtypeData t) { + return streamSubtypeComplement(t); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return streamSubtypeIsEmpty(cx, t); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/StringOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/StringOps.java new file mode 100644 index 000000000000..fd345ce17d6c --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/StringOps.java @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Common; +import io.ballerina.types.Context; +import io.ballerina.types.EnumerableCharString; +import io.ballerina.types.EnumerableString; +import io.ballerina.types.EnumerableSubtype; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.CharStringSubtype; +import io.ballerina.types.subtypedata.NonCharStringSubtype; +import io.ballerina.types.subtypedata.StringSubtype; + +import java.util.ArrayList; +import java.util.List; + +import static io.ballerina.types.EnumerableSubtype.EQ; +import static io.ballerina.types.EnumerableSubtype.GT; +import static io.ballerina.types.EnumerableSubtype.LT; + +/** + * Basic subtype ops for string type. + * + * @since 2201.8.0 + */ +public class StringOps implements BasicTypeOps { + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + //List values = new ArrayList<>(); + //boolean allowed = EnumerableSubtype.enumerableSubtypeUnion((StringSubtype) d1, (StringSubtype) d2, values); + //EnumerableString[] valueArray = new EnumerableString[values.size()]; + //return StringSubtype.createStringSubtype(allowed, values.toArray(valueArray)); + StringSubtype sd1 = (StringSubtype) d1; + StringSubtype sd2 = (StringSubtype) d2; + ArrayList chars = new ArrayList<>(); + ArrayList nonChars = new ArrayList<>(); + boolean charsAllowed = EnumerableSubtype.enumerableSubtypeUnion(sd1.getChar(), sd2.getChar(), chars); + boolean nonCharsAllowed = EnumerableSubtype.enumerableSubtypeUnion(sd1.getNonChar(), + sd2.getNonChar(), nonChars); + + return StringSubtype.createStringSubtype(CharStringSubtype.from(charsAllowed, + chars.toArray(new EnumerableCharString[]{})), + NonCharStringSubtype.from(nonCharsAllowed, nonChars.toArray(new EnumerableString[]{}))); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + if (d1 instanceof AllOrNothingSubtype) { + return ((AllOrNothingSubtype) d1).isAllSubtype() ? d2 : AllOrNothingSubtype.createNothing(); + } + if (d2 instanceof AllOrNothingSubtype) { + return ((AllOrNothingSubtype) d2).isAllSubtype() ? d1 : AllOrNothingSubtype.createNothing(); + } + + StringSubtype sd1 = (StringSubtype) d1; + StringSubtype sd2 = (StringSubtype) d2; + ArrayList chars = new ArrayList<>(); + ArrayList nonChars = new ArrayList<>(); + boolean charsAllowed = EnumerableSubtype.enumerableSubtypeIntersect(sd1.getChar(), sd2.getChar(), chars); + boolean nonCharsAllowed = EnumerableSubtype.enumerableSubtypeIntersect(sd1.getNonChar(), + sd2.getNonChar(), nonChars); + + return StringSubtype.createStringSubtype(CharStringSubtype.from(charsAllowed, + chars.toArray(new EnumerableCharString[]{})), + NonCharStringSubtype.from(nonCharsAllowed, nonChars.toArray(new EnumerableString[]{}))); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + return intersect(d1, complement(d2)); + } + + @Override + public SubtypeData complement(SubtypeData d) { + StringSubtype st = (StringSubtype) d; + if (st.getChar().values.length == 0 && st.getNonChar().values.length == 0) { + if (st.getChar().allowed && st.getNonChar().allowed) { + return AllOrNothingSubtype.createAll(); + } else if (!st.getChar().allowed && !st.getNonChar().allowed) { + return AllOrNothingSubtype.createNothing(); + } + } + + return StringSubtype.createStringSubtype(CharStringSubtype.from(!st.getChar().allowed, st.getChar().values), + NonCharStringSubtype.from(!st.getNonChar().allowed, st.getNonChar().values)); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + return Common.notIsEmpty(cx, t); + } + + // Returns a description of the relationship between a StringSubtype and a list of strings + // `values` must be ordered. + static StringSubtype.StringSubtypeListCoverage stringSubtypeListCoverage(StringSubtype subtype, String[] values) { + List indices = new ArrayList<>(); + CharStringSubtype ch = subtype.getChar(); + NonCharStringSubtype nonChar = subtype.getNonChar(); + int stringConsts = 0; + if (ch.allowed) { + stringListIntersect(values, toStringArray(ch.values), indices); + stringConsts = ch.values.length; + } else if (ch.values.length == 0) { + for (int i = 0; i < values.length; i++) { + if (values[i].length() == 1) { + indices.add(i); + } + } + } + if (nonChar.allowed) { + stringListIntersect(values, toStringArray(nonChar.values), indices); + stringConsts += nonChar.values.length; + } else if (nonChar.values.length == 0) { + for (int i = 0; i < values.length; i++) { + if (values[i].length() != 1) { + indices.add(i); + } + } + } + int[] inds = indices.stream().mapToInt(i -> i).toArray(); + return StringSubtype.StringSubtypeListCoverage.from(stringConsts == indices.size(), inds); + } + + private static String[] toStringArray(EnumerableCharString[] ar) { + String[] strings = new String[ar.length]; + for (int i = 0; i < ar.length; i++) { + strings[i] = ar[i].value; + } + return strings; + } + + private static String[] toStringArray(EnumerableString[] ar) { + String[] strings = new String[ar.length]; + for (int i = 0; i < ar.length; i++) { + strings[i] = ar[i].value; + } + return strings; + } + + static void stringListIntersect(String[] values, String[] target, List indices) { + int i1 = 0; + int i2 = 0; + int len1 = values.length; + int len2 = target.length; + while (true) { + if (i1 >= len1 || i2 >= len2) { + break; + } else { + int comp = EnumerableSubtype.compareEnumerable(EnumerableString.from(values[i1]), + EnumerableString.from(target[i2])); + switch (comp) { + case EQ: + indices.add(i1); + i1 += 1; + i2 += 1; + break; + case LT: + i1 += 1; + break; + case GT: + i2 += 1; + break; + default: + throw new AssertionError("Invalid comparison value!"); + } + } + } + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePair.java b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePair.java new file mode 100644 index 000000000000..fa21b980e681 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePair.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.ProperSubtypeData; + +/** + * Represent a 3-tuple containing paired-up subtype data. + * + * @since 2201.8.0 + */ +public class SubtypePair { + public final BasicTypeCode basicTypeCode; + public final ProperSubtypeData subtypeData1; + public final ProperSubtypeData subtypeData2; + + private SubtypePair(BasicTypeCode basicTypeCode, ProperSubtypeData subtypeData1, + ProperSubtypeData subtypeData2) { + this.basicTypeCode = basicTypeCode; + this.subtypeData1 = subtypeData1; + this.subtypeData2 = subtypeData2; + } + + public static SubtypePair create(BasicTypeCode basicTypeCode, + ProperSubtypeData subtypeData1, ProperSubtypeData subtypeData2) { + return new SubtypePair(basicTypeCode, subtypeData1, subtypeData2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairIterator.java b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairIterator.java new file mode 100644 index 000000000000..913f1ddc6ee0 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairIterator.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicSubtype; +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.ProperSubtypeData; +import io.ballerina.types.SemType; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.NoSuchElementException; + +/** + * Iteration implementation of `SubtypePairIterator`. + * + * @since 2201.8.0 + */ +public class SubtypePairIterator implements Iterator { + private int i1; + private int i2; + private final List t1; + private final List t2; + private final BasicTypeBitSet bits; + + private boolean doneIteration = false; + private boolean shouldCalculate = true; + private SubtypePair cache = null; + + public SubtypePairIterator(SemType t1, SemType t2, BasicTypeBitSet bits) { + this.i1 = 0; + this.i2 = 0; + this.t1 = unpackToBasicSubtypes(t1); + this.t2 = unpackToBasicSubtypes(t2); + this.bits = bits; + } + + private List unpackToBasicSubtypes(SemType type) { + if (type instanceof BasicTypeBitSet) { + return new ArrayList<>(); + } + return UnpackComplexSemType.unpack((ComplexSemType) type); + } + + private boolean include(BasicTypeCode code) { + return (this.bits.bitset & (1 << code.code)) != 0; + } + + private BasicSubtype get1() { + return this.t1.get(this.i1); + } + + private BasicSubtype get2() { + return this.t2.get(this.i2); + } + + @Override + public boolean hasNext() { + if (this.doneIteration) { + return false; + } + if (this.shouldCalculate) { + SubtypePair cache = internalNext(); + if (cache == null) { + this.doneIteration = true; + } + this.cache = cache; + this.shouldCalculate = false; + } + return !this.doneIteration; + } + + @Override + public SubtypePair next() { + if (this.doneIteration) { + throw new NoSuchElementException("Exhausted iterator"); + } + + if (this.shouldCalculate) { + SubtypePair cache = internalNext(); + if (cache == null) { + // this.doneIteration = true; + throw new IllegalStateException(); + } + this.cache = cache; + } + this.shouldCalculate = true; + return this.cache; + } + + /* + * This method corresponds to `next` method of SubtypePairIteratorImpl. + */ + private SubtypePair internalNext() { + while (true) { + if (this.i1 >= this.t1.size()) { + if (this.i2 >= this.t2.size()) { + break; + } + BasicSubtype t = get2(); + BasicTypeCode code = t.basicTypeCode; + ProperSubtypeData data2 = t.subtypeData; + this.i2 += 1; + if (include(code)) { + return SubtypePair.create(code, null, data2); + } + } else if (this.i2 >= this.t2.size()) { + BasicSubtype t = this.get1(); + this.i1 += 1; + BasicTypeCode code = t.basicTypeCode; + ProperSubtypeData data1 = t.subtypeData; + if (include(code)) { + return SubtypePair.create(code, data1, null); + } + } else { + BasicSubtype t1 = get1(); + BasicTypeCode code1 = t1.basicTypeCode; + ProperSubtypeData data1 = t1.subtypeData; + + BasicSubtype t2 = get2(); + BasicTypeCode code2 = t2.basicTypeCode; + ProperSubtypeData data2 = t2.subtypeData; + + if (code1.code == code2.code) { + this.i1 += 1; + this.i2 += 1; + if (include(code1)) { + return SubtypePair.create(code1, data1, data2); + } + } else if (code1.code < code2.code) { + this.i1 += 1; + if (include(code1)) { + return SubtypePair.create(code1, data1, null); + } + } else { + this.i2 += 1; + if (include(code2)) { + return SubtypePair.create(code2, null, data2); + } + } + } + } + return null; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairs.java b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairs.java new file mode 100644 index 000000000000..d703591efafa --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/SubtypePairs.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.SemType; + +import java.util.Iterator; + +/** + * Ballerina iterator is similar to an iterable in Java. + * This class implements the iterable for `SubtypePairIteratorImpl` + * + * @since 2201.8.0 + */ +public class SubtypePairs implements Iterable { + + private final SemType t1; + private final SemType t2; + private final BasicTypeBitSet bits; + + public SubtypePairs(SemType t1, SemType t2, BasicTypeBitSet bits) { + this.t1 = t1; + this.t2 = t2; + this.bits = bits; + } + + @Override + public Iterator iterator() { + return new SubtypePairIterator(t1, t2, bits); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/TableOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/TableOps.java new file mode 100644 index 000000000000..8d3f3430ad78 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/TableOps.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.Common.bddPosMaybeEmpty; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.PredefinedType.LIST_SUBTYPE_THREE_ELEMENT; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; +import static io.ballerina.types.typeops.ListOps.listSubtypeIsEmpty; + +/** + * Basic type ops for table type. + * + * @since 2201.8.0 + */ +public class TableOps extends CommonOps implements BasicTypeOps { + + private static SubtypeData tableSubtypeComplement(SubtypeData t) { + return bddSubtypeDiff(LIST_SUBTYPE_THREE_ELEMENT, t); + } + + private static boolean tableSubtypeIsEmpty(Context cx, SubtypeData t) { + Bdd b = (Bdd) t; + // The goal of this is to ensure that listSubtypeIsEmpty call beneath does + // not get an empty posList, because it will interpret that + // as `(any|error)[]` rather than `[(map)[], any|error, any|error]`. + b = bddPosMaybeEmpty(b) ? bddIntersect(b, LIST_SUBTYPE_THREE_ELEMENT) : b; + return listSubtypeIsEmpty(cx, b); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return tableSubtypeComplement(d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData d) { + return tableSubtypeIsEmpty(cx, d); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/TwoTuple.java b/semtypes/src/main/java/io/ballerina/types/typeops/TwoTuple.java new file mode 100644 index 000000000000..4490e3f23ed6 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/TwoTuple.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +/** + * Used to return two values from a method. + * + * @param Type of first item + * @param Type of second item + * @since 2201.8.0 + */ +public final class TwoTuple { + + final E1 item1; + final E2 item2; + + private TwoTuple(E1 item1, E2 item2) { + this.item1 = item1; + this.item2 = item2; + } + + public static TwoTuple from(E1 item1, E2 item2) { + return new TwoTuple<>(item1, item2); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/TypedescOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/TypedescOps.java new file mode 100644 index 000000000000..26f283b35a8f --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/TypedescOps.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Context; +import io.ballerina.types.SubtypeData; + +import static io.ballerina.types.Common.bddPosMaybeEmpty; +import static io.ballerina.types.Common.bddSubtypeDiff; +import static io.ballerina.types.PredefinedType.BDD_SUBTYPE_RO; +import static io.ballerina.types.typeops.BddCommonOps.bddIntersect; +import static io.ballerina.types.typeops.MappingOps.mappingSubtypeIsEmpty; + +/** + * Basic type ops for typedesc type. + * + * @since 2201.10.0 + */ +public class TypedescOps extends CommonOps implements BasicTypeOps { + + private static SubtypeData typedescSubtypeComplement(SubtypeData t) { + return bddSubtypeDiff(BDD_SUBTYPE_RO, t); + } + + private static boolean typedescSubtypeIsEmpty(Context cx, SubtypeData t) { + Bdd b = (Bdd) t; + // The goal of this is to ensure that mappingSubtypeIsEmpty call beneath does + // not get an empty posList, because it will interpret that + // as `map` rather than `readonly & map)`. + b = bddPosMaybeEmpty(b) ? bddIntersect(b, BDD_SUBTYPE_RO) : b; + return mappingSubtypeIsEmpty(cx, b); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return typedescSubtypeComplement(d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData d) { + return typedescSubtypeIsEmpty(cx, d); + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/UnpackComplexSemType.java b/semtypes/src/main/java/io/ballerina/types/typeops/UnpackComplexSemType.java new file mode 100644 index 000000000000..7cf850a46540 --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/UnpackComplexSemType.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicSubtype; +import io.ballerina.types.BasicTypeCode; +import io.ballerina.types.ComplexSemType; +import io.ballerina.types.ProperSubtypeData; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represent `unpackComplexSemType` function. + * + * @since 2201.8.0 + */ +public class UnpackComplexSemType { + private UnpackComplexSemType() { + } + + public static List unpack(ComplexSemType t) { + int some = t.some(); + List subtypeList = new ArrayList<>(); + for (ProperSubtypeData data : t.subtypeDataList()) { + int code = Integer.numberOfTrailingZeros(some); + subtypeList.add(BasicSubtype.from(BasicTypeCode.from(code), data)); + some ^= (1 << code); + } + return subtypeList; + } +} diff --git a/semtypes/src/main/java/io/ballerina/types/typeops/XmlOps.java b/semtypes/src/main/java/io/ballerina/types/typeops/XmlOps.java new file mode 100644 index 000000000000..4bd166c5a42e --- /dev/null +++ b/semtypes/src/main/java/io/ballerina/types/typeops/XmlOps.java @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types.typeops; + +import io.ballerina.types.BasicTypeOps; +import io.ballerina.types.Bdd; +import io.ballerina.types.Common; +import io.ballerina.types.Conjunction; +import io.ballerina.types.Context; +import io.ballerina.types.RecAtom; +import io.ballerina.types.SubtypeData; +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.subtypedata.XmlSubtype; + +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_ALL_MASK; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_RO_MASK; +import static io.ballerina.types.subtypedata.XmlSubtype.XML_PRIMITIVE_RO_SINGLETON; + +/** + * Basic subtype ops for xml type. + * + * @since 2201.8.0 + */ +public class XmlOps implements BasicTypeOps { + + public static final XmlSubtype XML_SUBTYPE_RO = XmlSubtype.from(XML_PRIMITIVE_RO_MASK, + BddCommonOps.bddAtom(RecAtom.createXMLRecAtom(XML_PRIMITIVE_RO_SINGLETON))); + public static final XmlSubtype XML_SUBTYPE_TOP = XmlSubtype.from(XML_PRIMITIVE_ALL_MASK, BddAllOrNothing.bddAll()); + + @Override + public SubtypeData union(SubtypeData d1, SubtypeData d2) { + XmlSubtype v1 = (XmlSubtype) d1; + XmlSubtype v2 = (XmlSubtype) d2; + int primitives = v1.primitives | v2.primitives; + return XmlSubtype.createXmlSubtype(primitives, BddCommonOps.bddUnion(v1.sequence, v2.sequence)); + } + + @Override + public SubtypeData intersect(SubtypeData d1, SubtypeData d2) { + XmlSubtype v1 = (XmlSubtype) d1; + XmlSubtype v2 = (XmlSubtype) d2; + int primitives = v1.primitives & v2.primitives; + return XmlSubtype.createXmlSubtypeOrEmpty(primitives, BddCommonOps.bddIntersect(v1.sequence, v2.sequence)); + } + + @Override + public SubtypeData diff(SubtypeData d1, SubtypeData d2) { + XmlSubtype v1 = (XmlSubtype) d1; + XmlSubtype v2 = (XmlSubtype) d2; + int primitives = v1.primitives & ~v2.primitives; + return XmlSubtype.createXmlSubtypeOrEmpty(primitives, BddCommonOps.bddDiff(v1.sequence, v2.sequence)); + } + + @Override + public SubtypeData complement(SubtypeData d) { + return diff(XML_SUBTYPE_TOP, d); + } + + @Override + public boolean isEmpty(Context cx, SubtypeData t) { + XmlSubtype sd = (XmlSubtype) t; + if (sd.primitives != 0) { + return false; + } + return xmlBddEmpty(cx, sd.sequence); + } + + boolean xmlBddEmpty(Context cx, Bdd bdd) { + return Common.bddEvery(cx, bdd, null, null, XmlOps::xmlFormulaIsEmpty); + } + + private static boolean xmlFormulaIsEmpty(Context cx, Conjunction pos, Conjunction neg) { + int allPosBits = collectAllPrimitives(pos) & XmlSubtype.XML_PRIMITIVE_ALL_MASK; + return xmlHasTotalNegative(allPosBits, neg); + } + + public static int collectAllPrimitives(Conjunction con) { + int bits = 0; + Conjunction current = con; + while (current != null) { + bits &= getIndex(current); + current = current.next; + } + return bits; + } + + public static boolean xmlHasTotalNegative(int allBits, Conjunction con) { + if (allBits == 0) { + return true; + } + + Conjunction n = con; + while (n != null) { + if ((allBits & ~getIndex(con)) == 0) { + return true; + } + n = n.next; + } + return false; + } + + private static int getIndex(Conjunction con) { + return ((RecAtom) con.atom).index; + } +} diff --git a/semtypes/src/main/java/module-info.java b/semtypes/src/main/java/module-info.java index 5ae00697cfae..40129cf5658d 100644 --- a/semtypes/src/main/java/module-info.java +++ b/semtypes/src/main/java/module-info.java @@ -1,3 +1,5 @@ module io.ballerina.semtype { - + exports io.ballerina.types; + exports io.ballerina.types.definition; + exports io.ballerina.types.subtypedata; } diff --git a/semtypes/src/test/java/io/ballerina/semtype/SemTypeTest.java b/semtypes/src/test/java/io/ballerina/semtype/SemTypeTest.java deleted file mode 100644 index a4a16c8e6031..000000000000 --- a/semtypes/src/test/java/io/ballerina/semtype/SemTypeTest.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache License, - * Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package io.ballerina.semtype; - -import org.testng.Assert; -import org.testng.annotations.Test; - -/** - * Tests if placeholder is created. - * - */ -public class SemTypeTest { - - @SuppressWarnings("ConstantValue") - @Test - public void testSemType() { - SemTypeMock s1 = new UniformTypeBitSetMock(0x00); - SemTypeMock s2 = new ComplexSemTypeMock(); - Assert.assertTrue(s1 instanceof UniformTypeBitSetMock); - Assert.assertTrue(s2 instanceof ComplexSemTypeMock); - } -} diff --git a/semtypes/src/test/java/io/ballerina/types/CellTypeTest.java b/semtypes/src/test/java/io/ballerina/types/CellTypeTest.java new file mode 100644 index 000000000000..8da46f516ca3 --- /dev/null +++ b/semtypes/src/test/java/io/ballerina/types/CellTypeTest.java @@ -0,0 +1,361 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.CellSubtype; +import org.testng.Assert; +import org.testng.annotations.AfterClass; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_LIMITED; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_NONE; +import static io.ballerina.types.CellAtomicType.CellMutability.CELL_MUT_UNLIMITED; + +/** + * Tests subtyping rules of cell basic type. + * + * @since 2201.10.0 + */ +public class CellTypeTest { + + Context ctx; + + public enum Relation { + EQUAL("="), + SUBTYPE("<"), + NO_RELATION("<>"); + + final String value; + + Relation(String value) { + this.value = value; + } + } + + @BeforeClass + public void beforeClass() { + ctx = Context.from(new Env()); + } + + private CellSemType cell(SemType ty, CellAtomicType.CellMutability mut) { + return CellSubtype.cellContaining(ctx.env, ty, mut); + } + + private void assertSemTypeRelation(SemType t1, SemType t2, Relation relation) { + Relation actual = getSemTypeRelation(t1, t2); + Assert.assertEquals(actual, relation); + } + + private Relation getSemTypeRelation(SemType t1, SemType t2) { + boolean s1 = Core.isSubtype(ctx, t1, t2); + boolean s2 = Core.isSubtype(ctx, t2, t1); + if (s1 && s2) { + return Relation.EQUAL; + } else if (s1) { + return Relation.SUBTYPE; + } else if (s2) { + throw new IllegalStateException("'>' relation found which can be converted to a '<' relation"); + } else { + return Relation.NO_RELATION; + } + } + + @Test(description = "Test T and cell(T) having no relation", dataProvider = "typeCellDisparityDataProvider") + public void testTypeCellDisparity(SemType t1, SemType t2, Relation relation) { + assertSemTypeRelation(t1, t2, relation); + } + + @DataProvider(name = "typeCellDisparityDataProvider") + public Object[][] createTypeCellDisparityTestData() { + return new Object[][]{ + {PredefinedType.INT, cell(PredefinedType.INT, CELL_MUT_NONE), Relation.NO_RELATION}, + {PredefinedType.INT, cell(PredefinedType.INT, CELL_MUT_LIMITED), Relation.NO_RELATION}, + {PredefinedType.INT, cell(PredefinedType.INT, CELL_MUT_UNLIMITED), Relation.NO_RELATION}, + }; + } + + @Test(description = "Test basic cell subtyping", dataProvider = "basicCellSubtypingDataProvider") + public void testBasicCellSubtyping(SemType t1, SemType t2, Relation[] relations) { + assert relations.length == 3; + Relation[] actual = new Relation[3]; + + CellAtomicType.CellMutability[] values = CellAtomicType.CellMutability.values(); + // Obtaining relation for each mutability kind + for (int i = 0; i < values.length; i++) { + CellAtomicType.CellMutability mut = values[i]; + CellSemType c1 = cell(t1, mut); + CellSemType c2 = cell(t2, mut); + actual[i] = getSemTypeRelation(c1, c2); + } + + Assert.assertEquals(actual, relations); + } + + @DataProvider(name = "basicCellSubtypingDataProvider") + public Object[][] createBasicCellSubtypingTestData() { + // This contains some of nBallerina 'cell-1.typetest' test data + return new Object[][]{ + { + PredefinedType.INT, PredefinedType.INT, + new Relation[]{ + Relation.EQUAL, Relation.EQUAL, Relation.EQUAL + } + }, + { + PredefinedType.BOOLEAN, PredefinedType.BOOLEAN, + new Relation[]{ + Relation.EQUAL, Relation.EQUAL, Relation.EQUAL + } + }, + { + PredefinedType.BYTE, PredefinedType.INT, + new Relation[]{ + Relation.SUBTYPE, Relation.SUBTYPE, Relation.SUBTYPE + } + }, + { + PredefinedType.BOOLEAN, PredefinedType.INT, + new Relation[]{ + Relation.NO_RELATION, Relation.NO_RELATION, Relation.NO_RELATION + } + }, + { + PredefinedType.BOOLEAN, Core.union(PredefinedType.INT, PredefinedType.BOOLEAN), + new Relation[]{ + Relation.SUBTYPE, Relation.SUBTYPE, Relation.SUBTYPE + } + } + }; + } + + @Test(dataProvider = "cellSubtypeDataProvider1") + public void testCellSubtyping1(SemType t1, SemType t2, Relation relation) { + assertSemTypeRelation(t1, t2, relation); + } + + @DataProvider(name = "cellSubtypeDataProvider1") + public Object[][] createCellSubtypeData1() { + // This contains some of nBallerina 'cell-1.typetest' test data + return new Object[][]{ + // Set 1 + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.BOOLEAN, CELL_MUT_NONE) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN), CELL_MUT_NONE), + Relation.EQUAL + }, + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.BOOLEAN, CELL_MUT_LIMITED) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN), CELL_MUT_LIMITED), + Relation.SUBTYPE + }, + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_UNLIMITED), + cell(PredefinedType.BOOLEAN, CELL_MUT_UNLIMITED) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN), CELL_MUT_UNLIMITED), + Relation.EQUAL + }, + // Set 2 + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.BOOLEAN, CELL_MUT_NONE), + cell(PredefinedType.STRING, CELL_MUT_NONE) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_NONE), + Relation.EQUAL + }, + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.BOOLEAN, CELL_MUT_LIMITED), + cell(PredefinedType.STRING, CELL_MUT_LIMITED) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_LIMITED), + Relation.SUBTYPE + }, + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_UNLIMITED), + cell(PredefinedType.BOOLEAN, CELL_MUT_UNLIMITED), + cell(PredefinedType.STRING, CELL_MUT_UNLIMITED) + ), + cell(SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_UNLIMITED), + Relation.EQUAL + }, + // Set 3 + { + SemTypes.union( + cell(TypeTestUtils.roTuple(ctx.env, PredefinedType.INT), CELL_MUT_NONE), + cell(TypeTestUtils.roTuple(ctx.env, PredefinedType.BOOLEAN), CELL_MUT_NONE) + ), + cell(TypeTestUtils.roTuple(ctx.env, SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN)), + CELL_MUT_NONE), + Relation.EQUAL + }, + { + SemTypes.union( + cell(TypeTestUtils.tuple(ctx.env, PredefinedType.INT), CELL_MUT_LIMITED), + cell(TypeTestUtils.tuple(ctx.env, PredefinedType.BOOLEAN), CELL_MUT_LIMITED) + ), + cell(TypeTestUtils.tuple(ctx.env, SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN)), + CELL_MUT_LIMITED), + Relation.SUBTYPE + }, + { + SemTypes.union( + cell(TypeTestUtils.tuple(ctx.env, PredefinedType.INT), CELL_MUT_UNLIMITED), + cell(TypeTestUtils.tuple(ctx.env, PredefinedType.BOOLEAN), CELL_MUT_UNLIMITED) + ), + cell(TypeTestUtils.tuple(ctx.env, SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN)), + CELL_MUT_UNLIMITED), + Relation.SUBTYPE + }, + }; + } + + @Test(dataProvider = "cellSubtypeDataProvider2") + public void testCellSubtyping2(SemType t1, SemType t2, Relation relation) { + assertSemTypeRelation(t1, t2, relation); + } + + @DataProvider(name = "cellSubtypeDataProvider2") + public Object[][] createCellSubtypeData2() { + // This contains nBallerina 'cell-2.typetest' test data + return new Object[][]{ + // test 1 + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.BOOLEAN, CELL_MUT_UNLIMITED), + cell(PredefinedType.STRING, CELL_MUT_LIMITED) + ), + cell( + SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_UNLIMITED + ), + Relation.SUBTYPE + }, + // test 2 + { + cell( + SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_NONE + ), + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.BOOLEAN, CELL_MUT_UNLIMITED), + cell(PredefinedType.STRING, CELL_MUT_LIMITED) + ), + Relation.SUBTYPE + }, + // test 3 + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.BOOLEAN, CELL_MUT_UNLIMITED), + cell(PredefinedType.STRING, CELL_MUT_LIMITED) + ), + cell( + SemTypes.union(PredefinedType.INT, PredefinedType.BOOLEAN, PredefinedType.STRING), + CELL_MUT_LIMITED + ), + Relation.NO_RELATION + }, + // test 4 + { + SemTypes.union( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED) + ), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED), + Relation.EQUAL + }, + // test 5 + { + SemTypes.intersect( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED) + ), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED), + Relation.SUBTYPE + }, + // test 6 + { + SemTypes.intersect( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED) + ), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + Relation.SUBTYPE + }, + // test 7 + { + SemTypes.intersect( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.INT, CELL_MUT_UNLIMITED) + ), + cell(PredefinedType.INT, CELL_MUT_NONE), + Relation.EQUAL + }, + // test 8 + { + SemTypes.intersect( + cell(PredefinedType.INT, CELL_MUT_NONE), + cell(PredefinedType.INT, CELL_MUT_LIMITED), + cell(PredefinedType.BYTE, CELL_MUT_LIMITED) + ), + cell(PredefinedType.BYTE, CELL_MUT_LIMITED), + Relation.SUBTYPE + }, + // test 9 + { + SemTypes.intersect( + cell(PredefinedType.INT, CELL_MUT_NONE), + SemTypes.union( + cell(PredefinedType.BYTE, CELL_MUT_LIMITED), + cell(PredefinedType.BOOLEAN, CELL_MUT_LIMITED) + ) + ), + cell(PredefinedType.BYTE, CELL_MUT_NONE), + Relation.EQUAL + }, + }; + } + + @AfterClass + public void afterClass() { + ctx = null; + } +} diff --git a/semtypes/src/test/java/io/ballerina/types/EnvInitTest.java b/semtypes/src/test/java/io/ballerina/types/EnvInitTest.java new file mode 100644 index 000000000000..1cf69e55251e --- /dev/null +++ b/semtypes/src/test/java/io/ballerina/types/EnvInitTest.java @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import org.testng.Assert; +import org.testng.annotations.BeforeClass; +import org.testng.annotations.Test; + +import java.lang.ref.Reference; +import java.lang.reflect.Field; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import static io.ballerina.types.Core.union; + +/** + * Test class for {@link Env} initialization. + * + * @since 2201.10.0 + */ +public class EnvInitTest { + + @BeforeClass + public void setup() { + // All the types in PredefinedTypeEnv are populated by the loading of PredefinedType class. + try { + Class.forName("io.ballerina.types.PredefinedType"); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Test + public void testEnvInitAtomTable() throws NoSuchFieldException, IllegalAccessException { + final Env env = new Env(); + + // Access the private field atomTable using reflection + Field atomTableField = Env.class.getDeclaredField("atomTable"); + atomTableField.setAccessible(true); + Map> atomTable = (Map) atomTableField.get(env); + + // Check that the atomTable contains the expected entries + Assert.assertEquals(atomTable.size(), 19); + + CellAtomicType cellAtomicVal = CellAtomicType.from( + PredefinedType.VAL, CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + + TypeAtom typeAtom0 = atomTable.get(cellAtomicVal).get(); + Assert.assertNotNull(typeAtom0); + Assert.assertEquals(typeAtom0.atomicType(), cellAtomicVal); + + CellAtomicType cellAtomicNever = CellAtomicType.from( + PredefinedType.NEVER, CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + + TypeAtom typeAtom1 = atomTable.get(cellAtomicNever).get(); + Assert.assertNotNull(typeAtom1); + Assert.assertEquals(typeAtom1.atomicType(), cellAtomicNever); + + CellAtomicType cellAtomicInner = CellAtomicType.from( + PredefinedType.INNER, CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + + TypeAtom typeAtom2 = atomTable.get(cellAtomicInner).get(); + Assert.assertNotNull(typeAtom2); + Assert.assertEquals(typeAtom2.atomicType(), cellAtomicInner); + + CellAtomicType cellAtomicInnerMapping = CellAtomicType.from( + union(PredefinedType.MAPPING, PredefinedType.UNDEF), + CellAtomicType.CellMutability.CELL_MUT_LIMITED + ); + + TypeAtom typeAtom3 = atomTable.get(cellAtomicInnerMapping).get(); + Assert.assertNotNull(typeAtom3); + Assert.assertEquals(typeAtom3.atomicType(), cellAtomicInnerMapping); + + ListAtomicType listAtomicMapping = ListAtomicType.from( + FixedLengthArray.empty(), PredefinedType.CELL_SEMTYPE_INNER_MAPPING + ); + + TypeAtom typeAtom4 = atomTable.get(listAtomicMapping).get(); + Assert.assertNotNull(typeAtom4); + Assert.assertEquals(typeAtom4.atomicType(), listAtomicMapping); + + TypeAtom typeAtom5 = atomTable.get(PredefinedType.CELL_ATOMIC_INNER_MAPPING_RO).get(); + Assert.assertNotNull(typeAtom5); + Assert.assertEquals(typeAtom5.atomicType(), PredefinedType.CELL_ATOMIC_INNER_MAPPING_RO); + + ListAtomicType listAtomicMappingRo = ListAtomicType.from( + FixedLengthArray.empty(), PredefinedType.CELL_SEMTYPE_INNER_MAPPING_RO + ); + + TypeAtom typeAtom6 = atomTable.get(listAtomicMappingRo).get(); + Assert.assertNotNull(typeAtom6); + Assert.assertEquals(typeAtom6.atomicType(), listAtomicMappingRo); + + CellAtomicType cellAtomicInnerRo = CellAtomicType.from( + PredefinedType.INNER_READONLY, CellAtomicType.CellMutability.CELL_MUT_NONE + ); + + TypeAtom typeAtom7 = atomTable.get(cellAtomicInnerRo).get(); + Assert.assertNotNull(typeAtom7); + Assert.assertEquals(typeAtom7.atomicType(), cellAtomicInnerRo); + + CellAtomicType cellAtomicUndef = CellAtomicType.from( + PredefinedType.UNDEF, CellAtomicType.CellMutability.CELL_MUT_NONE + ); + + TypeAtom typeAtom8 = atomTable.get(cellAtomicUndef).get(); + Assert.assertNotNull(typeAtom8); + Assert.assertEquals(typeAtom8.atomicType(), cellAtomicUndef); + + ListAtomicType listAtomicTwoElement = ListAtomicType.from( + FixedLengthArray.from(List.of(PredefinedType.CELL_SEMTYPE_VAL), 2), + PredefinedType.CELL_SEMTYPE_UNDEF + ); + + TypeAtom typeAtom9 = atomTable.get(listAtomicTwoElement).get(); + Assert.assertNotNull(typeAtom8); + Assert.assertEquals(typeAtom9.atomicType(), listAtomicTwoElement); + } + + @Test + public void testTypeAtomIndices() throws NoSuchFieldException, IllegalAccessException { + Env env = new Env(); + Field atomTableField = Env.class.getDeclaredField("atomTable"); + atomTableField.setAccessible(true); + Map> recListAtoms = + (Map>) atomTableField.get(env); + Collection indices = new HashSet<>(); + for (var each : recListAtoms.values()) { + Assert.assertTrue(indices.add(each.get().index()), "Duplicate index found: " + each.get().index()); + } + } + + @Test + public void testEnvInitRecAtoms() throws NoSuchFieldException, IllegalAccessException { + Env env = new Env(); + Field recListAtomsField = Env.class.getDeclaredField("recListAtoms"); + recListAtomsField.setAccessible(true); + List recListAtoms = (List) recListAtomsField.get(env); + Assert.assertEquals(recListAtoms.size(), 2); + ListAtomicType listAtomicRo = ListAtomicType.from( + FixedLengthArray.empty(), PredefinedType.CELL_SEMTYPE_INNER_RO + ); + Assert.assertEquals(recListAtoms.get(0), listAtomicRo); + Assert.assertNull(recListAtoms.get(1)); + + Field recMappingAtomsField = Env.class.getDeclaredField("recMappingAtoms"); + recMappingAtomsField.setAccessible(true); + List recMappingAtoms = (List) recMappingAtomsField.get(env); + Assert.assertEquals(recMappingAtoms.size(), 2); + Assert.assertEquals(recMappingAtoms.get(0), PredefinedType.MAPPING_ATOMIC_RO); + Assert.assertEquals(recMappingAtoms.get(1), PredefinedType.MAPPING_ATOMIC_OBJECT_RO); + + Field recFunctionAtomsField = Env.class.getDeclaredField("recFunctionAtoms"); + recFunctionAtomsField.setAccessible(true); + List recFunctionAtoms = (List) recFunctionAtomsField.get(env); + Assert.assertEquals(recFunctionAtoms.size(), 0); + } +} diff --git a/semtypes/src/test/java/io/ballerina/types/SemTypeBddTest.java b/semtypes/src/test/java/io/ballerina/types/SemTypeBddTest.java new file mode 100644 index 000000000000..0653955dd696 --- /dev/null +++ b/semtypes/src/test/java/io/ballerina/types/SemTypeBddTest.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.subtypedata.BddAllOrNothing; +import io.ballerina.types.typeops.BddCommonOps; +import org.testng.Assert; +import org.testng.annotations.Test; + +/** + * Tests Bdd of Semtypes. + * + * @since 2201.8.0 + */ +public class SemTypeBddTest { + + @Test + public void bddTest() { + Bdd b1 = BddCommonOps.bddAtom(RecAtom.createRecAtom(1)); + Bdd b2 = BddCommonOps.bddAtom(RecAtom.createRecAtom(2)); + Bdd b1and2 = BddCommonOps.bddIntersect(b1, b2); + Bdd r = BddCommonOps.bddDiff(b1and2, b1); + Assert.assertFalse(((BddAllOrNothing) r).isAll()); + } +} diff --git a/semtypes/src/test/java/io/ballerina/types/SemTypeCoreTest.java b/semtypes/src/test/java/io/ballerina/types/SemTypeCoreTest.java new file mode 100644 index 000000000000..b9b15d2b1953 --- /dev/null +++ b/semtypes/src/test/java/io/ballerina/types/SemTypeCoreTest.java @@ -0,0 +1,393 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (https://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.types; + +import io.ballerina.types.definition.FunctionDefinition; +import io.ballerina.types.definition.FunctionQualifiers; +import io.ballerina.types.definition.ListDefinition; +import io.ballerina.types.subtypedata.AllOrNothingSubtype; +import io.ballerina.types.subtypedata.IntSubtype; +import io.ballerina.types.subtypedata.Range; +import io.ballerina.types.subtypedata.StringSubtype; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.function.BiFunction; + +import static io.ballerina.types.TypeTestUtils.roTuple; +import static io.ballerina.types.TypeTestUtils.tuple; +import static io.ballerina.types.subtypedata.StringSubtype.stringConst; + +/** + * Tests Core functions of Semtypes. + * + * @since 2201.8.0 + */ +public class SemTypeCoreTest { + + @Test + public void testSubtypeSimple() { + Assert.assertTrue(Core.isSubtypeSimple(PredefinedType.NIL, PredefinedType.ANY)); + Assert.assertTrue(Core.isSubtypeSimple(PredefinedType.INT, PredefinedType.VAL)); + Assert.assertTrue(Core.isSubtypeSimple(PredefinedType.ANY, PredefinedType.VAL)); + Assert.assertFalse(Core.isSubtypeSimple(PredefinedType.INT, PredefinedType.BOOLEAN)); + Assert.assertFalse(Core.isSubtypeSimple(PredefinedType.ERROR, PredefinedType.ANY)); + } + + @Test + public void testSingleNumericType() { + Assert.assertEquals(Core.singleNumericType(PredefinedType.INT), Optional.of(PredefinedType.INT)); + Assert.assertEquals(Core.singleNumericType(PredefinedType.BOOLEAN), Optional.empty()); + Core.singleNumericType(Core.singleton(1L)); + Assert.assertEquals(Core.singleNumericType(Core.singleton(1L)), Optional.of(PredefinedType.INT)); + Assert.assertEquals(Core.singleNumericType(Core.union(PredefinedType.INT, PredefinedType.FLOAT)), + Optional.empty()); + } + + @Test + public void testBitTwiddling() { + Assert.assertEquals(Long.numberOfTrailingZeros(0x10), 4); + Assert.assertEquals(Long.numberOfTrailingZeros(0x100), 8); + Assert.assertEquals(Long.numberOfTrailingZeros(0x1), 0); + Assert.assertEquals(Long.numberOfTrailingZeros(0x0), 64); + Assert.assertEquals(Integer.bitCount(0x10000), 1); + Assert.assertEquals(Integer.bitCount(0), 0); + Assert.assertEquals(Integer.bitCount(1), 1); + Assert.assertEquals(Integer.bitCount(0x10010010), 3); + } + + @Test + public void test1() { + Env env = new Env(); + disjoint(Core.typeCheckContext(env), PredefinedType.STRING, PredefinedType.INT); + disjoint(Core.typeCheckContext(env), PredefinedType.INT, PredefinedType.NIL); + SemType t1 = createTupleType(env, PredefinedType.INT, PredefinedType.INT); + disjoint(Core.typeCheckContext(env), t1, PredefinedType.INT); + SemType t2 = createTupleType(env, PredefinedType.STRING, PredefinedType.STRING); + disjoint(Core.typeCheckContext(env), PredefinedType.NIL, t2); + } + + private void disjoint(Context cx, SemType t1, SemType t2) { + Assert.assertFalse(Core.isSubtype(cx, t1, t2)); + Assert.assertFalse(Core.isSubtype(cx, t2, t1)); + Assert.assertTrue(Core.isEmpty(cx, Core.intersect(t1, t2))); + } + + @Test + public void test2() { + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(new Env()), PredefinedType.INT, PredefinedType.VAL)); + } + + @Test + public void test3() { + Env env = new Env(); + SemType s = roTuple(env, PredefinedType.INT, Core.union(PredefinedType.INT, + PredefinedType.STRING)); + SemType t = Core.union(roTuple(env, PredefinedType.INT, PredefinedType.INT), + roTuple(env, PredefinedType.INT, PredefinedType.STRING)); + equiv(env, s, t); + } + + private void equiv(Env env, SemType s, SemType t) { + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void test4() { + Env env = new Env(); + SemType isT = createTupleType(env, PredefinedType.INT, PredefinedType.STRING); + SemType itT = createTupleType(env, PredefinedType.INT, PredefinedType.VAL); + SemType tsT = createTupleType(env, PredefinedType.VAL, PredefinedType.STRING); + SemType iiT = createTupleType(env, PredefinedType.INT, PredefinedType.INT); + SemType ttT = createTupleType(env, PredefinedType.VAL, PredefinedType.VAL); + Context cx = Core.typeCheckContext(env); + Assert.assertTrue(Core.isSubtype(cx, isT, itT)); + Assert.assertTrue(Core.isSubtype(cx, isT, tsT)); + Assert.assertTrue(Core.isSubtype(cx, iiT, ttT)); + } + + @Test + public void test5() { + Env env = new Env(); + SemType s = roTuple(env, PredefinedType.INT, Core.union(PredefinedType.NIL, + Core.union(PredefinedType.INT, PredefinedType.STRING))); + SemType t = Core.union(roTuple(env, PredefinedType.INT, PredefinedType.INT), + Core.union(roTuple(env, PredefinedType.INT, PredefinedType.NIL), + roTuple(env, PredefinedType.INT, PredefinedType.STRING))); + equiv(env, s, t); + } + + @Test + public void test6() { + Env env = new Env(); + SemType s = tuple(env, PredefinedType.INT, Core.union(PredefinedType.NIL, + Core.union(PredefinedType.INT, PredefinedType.STRING))); + SemType t = Core.union(tuple(env, PredefinedType.INT, PredefinedType.INT), + Core.union(tuple(env, PredefinedType.INT, PredefinedType.NIL), + tuple(env, PredefinedType.INT, PredefinedType.STRING))); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t, s)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), s, t)); + } + + @Test + public void test7() { + Env env = new Env(); + SemType s = tuple(env, PredefinedType.INT, Core.union(PredefinedType.INT, + PredefinedType.STRING)); + SemType t = Core.union(tuple(env, PredefinedType.INT, PredefinedType.INT), + tuple(env, PredefinedType.INT, PredefinedType.STRING)); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t, s)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), s, t)); + } + + @Test + public void tupleTest1() { + Env env = new Env(); + SemType s = createTupleType(env, PredefinedType.INT, PredefinedType.STRING, PredefinedType.NIL); + SemType t = createTupleType(env, PredefinedType.VAL, PredefinedType.VAL, PredefinedType.VAL); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void tupleTest2() { + Env env = new Env(); + SemType s = createTupleType(env, PredefinedType.INT, PredefinedType.STRING, PredefinedType.NIL); + SemType t = createTupleType(env, PredefinedType.VAL, PredefinedType.VAL); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void tupleTest3() { + Env env = new Env(); + SemType z1 = createTupleType(env); + SemType z2 = createTupleType(env); + SemType t = createTupleType(env, PredefinedType.INT); + Assert.assertTrue(!Core.isEmpty(Core.typeCheckContext(env), z1)); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), z1, z2)); + Assert.assertTrue(Core.isEmpty(Core.typeCheckContext(env), Core.diff(z1, z2))); + Assert.assertFalse(Core.isEmpty(Core.typeCheckContext(env), Core.diff(z1, PredefinedType.INT))); + Assert.assertFalse(Core.isEmpty(Core.typeCheckContext(env), Core.diff(PredefinedType.INT, z1))); + } + + @Test + public void tupleTest4() { + Env env = new Env(); + SemType s = createTupleType(env, PredefinedType.INT, PredefinedType.INT); + SemType t = createTupleType(env, PredefinedType.INT, PredefinedType.INT, PredefinedType.INT); + Assert.assertFalse(Core.isEmpty(Core.typeCheckContext(env), s)); + Assert.assertFalse(Core.isEmpty(Core.typeCheckContext(env), t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + Assert.assertTrue(Core.isEmpty(Core.typeCheckContext(env), Core.intersect(s, t))); + } + + private SemType func(Env env, SemType args, SemType ret) { + FunctionDefinition def = new FunctionDefinition(); + return def.define(env, args, ret, FunctionQualifiers.from(env, false, false)); + } + + @Test + public void funcTest1() { + Env env = new Env(); + SemType s = func(env, PredefinedType.INT, PredefinedType.INT); + SemType t = func(env, PredefinedType.INT, Core.union(PredefinedType.NIL, PredefinedType.INT)); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void funcTest2() { + Env env = new Env(); + SemType s = func(env, Core.union(PredefinedType.NIL, PredefinedType.INT), PredefinedType.INT); + SemType t = func(env, PredefinedType.INT, PredefinedType.INT); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void funcTest3() { + Env env = new Env(); + SemType s = func(env, createTupleType(env, Core.union(PredefinedType.NIL, PredefinedType.INT)), + PredefinedType.INT); + SemType t = func(env, createTupleType(env, PredefinedType.INT), PredefinedType.INT); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void funcTest4() { + Env env = new Env(); + SemType s = func(env, createTupleType(env, Core.union(PredefinedType.NIL, PredefinedType.INT)), + PredefinedType.INT); + SemType t = func(env, createTupleType(env, PredefinedType.INT), + Core.union(PredefinedType.NIL, PredefinedType.INT)); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), s, t)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t, s)); + } + + @Test + public void stringTest() { + List result = new ArrayList<>(); + // TODO may have to assert lists by converting the output to a string list + + EnumerableSubtype.enumerableListUnion(new EnumerableString[]{EnumerableString.from("a"), + EnumerableString.from("b"), EnumerableString.from("d")}, + new EnumerableString[]{EnumerableString.from("c")}, result); + Assert.assertEquals(result.get(0).value, "a"); + Assert.assertEquals(result.get(1).value, "b"); + Assert.assertEquals(result.get(2).value, "c"); + Assert.assertEquals(result.get(3).value, "d"); + + result = new ArrayList<>(); + EnumerableSubtype.enumerableListIntersect(new EnumerableString[]{EnumerableString.from("a"), + EnumerableString.from("b"), EnumerableString.from("d")}, + new EnumerableString[]{EnumerableString.from("d")}, result); + Assert.assertEquals(result.get(0).value, "d"); + + result = new ArrayList<>(); + EnumerableSubtype.enumerableListDiff(new EnumerableString[]{EnumerableString.from("a"), + EnumerableString.from("b"), EnumerableString.from("c"), EnumerableString.from("d")}, + new EnumerableString[]{EnumerableString.from("a"), EnumerableString.from("c")}, result); + Assert.assertEquals(result.get(0).value, "b"); + Assert.assertEquals(result.get(1).value, "d"); + } + + @Test + public void roListTest() { + SemType t1 = Core.intersect(PredefinedType.LIST, PredefinedType.VAL_READONLY); + Env env = new Env(); + ListDefinition ld = new ListDefinition(); + SemType t2 = ld.defineListTypeWrapped(env, new ArrayList<>(), 0, PredefinedType.VAL); + SemType t = Core.diff(t1, t2); + Context cx = Core.typeCheckContext(env); + boolean b = Core.isEmpty(cx, t); + Assert.assertTrue(b); + } + + @Test + public void testIntSubtypeWidenUnsigned() { + Assert.assertTrue(((AllOrNothingSubtype) IntSubtype.intSubtypeWidenUnsigned(AllOrNothingSubtype.createAll())) + .isAllSubtype()); + Assert.assertTrue(((AllOrNothingSubtype) IntSubtype.intSubtypeWidenUnsigned( + IntSubtype.createIntSubtype(new Range(-1L, 10L)))).isAllSubtype()); + IntSubtype intType1 = (IntSubtype) IntSubtype.intSubtypeWidenUnsigned( + IntSubtype.createIntSubtype(new Range(0L, 0L))); + Assert.assertEquals(intType1.ranges[0].min, 0L); + Assert.assertEquals(intType1.ranges[0].max, 255L); + IntSubtype intType2 = (IntSubtype) IntSubtype.intSubtypeWidenUnsigned( + IntSubtype.createIntSubtype(new Range(0L, 257L))); + Assert.assertEquals(intType2.ranges[0].min, 0L); + Assert.assertEquals(intType2.ranges[0].max, 65535L); + } + + public SemType recursiveTuple(Env env, BiFunction> f) { + ListDefinition def = new ListDefinition(); + SemType t = def.getSemType(env); + List members = f.apply(env, t); + return def.defineListTypeWrapped(env, members, members.size()); + } + + @Test + public void recTest() { + Env env = new Env(); + SemType t1 = recursiveTuple(env, + (e, t) -> Arrays.asList(PredefinedType.INT, Core.union(t, PredefinedType.NIL))); + SemType t2 = recursiveTuple(env, (e, t) -> Arrays.asList(Core.union(PredefinedType.INT, PredefinedType.STRING), + Core.union(t, PredefinedType.NIL))); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t1, t2)); + Assert.assertFalse(Core.isSubtype(Core.typeCheckContext(env), t2, t1)); + } + + @Test + public void recTest2() { + Env env = new Env(); + SemType t1 = Core.union(PredefinedType.NIL, recursiveTuple(env, + (e, t) -> Arrays.asList(PredefinedType.INT, Core.union(t, PredefinedType.NIL)))); + SemType t2 = recursiveTuple(env, + (e, t) -> Arrays.asList(PredefinedType.INT, Core.union(t, PredefinedType.NIL))); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t2, t1)); + } + + @Test + public void recTest3() { + Env env = new Env(); + SemType t1 = recursiveTuple(env, + (e, t) -> Arrays.asList(PredefinedType.INT, Core.union(t, PredefinedType.NIL))); + SemType t2 = recursiveTuple(env, (e, t) -> Arrays.asList(PredefinedType.INT, Core.union(PredefinedType.NIL, + createTupleType(e, PredefinedType.INT, Core.union(PredefinedType.NIL, t))))); + Assert.assertTrue(Core.isSubtype(Core.typeCheckContext(env), t1, t2)); + } + + @Test + public void testStringCharSubtype() { + ComplexSemType st = (ComplexSemType) stringConst("a"); + Assert.assertEquals(st.subtypeDataList().length, 1); + StringSubtype subType = (StringSubtype) st.subtypeDataList()[0]; + Assert.assertEquals(subType.getChar().values.length, 1); + Assert.assertEquals(subType.getChar().values[0].value, "a"); + Assert.assertEquals(subType.getChar().allowed, true); + Assert.assertEquals(subType.getNonChar().values.length, 0); + Assert.assertEquals(subType.getNonChar().allowed, true); + } + + @Test + public void testStringNonCharSubtype() { + ComplexSemType st = (ComplexSemType) stringConst("abc"); + Assert.assertEquals(st.subtypeDataList().length, 1); + StringSubtype subType = (StringSubtype) st.subtypeDataList()[0]; + Assert.assertEquals(subType.getChar().values.length, 0); + Assert.assertEquals(subType.getChar().allowed, true); + Assert.assertEquals(subType.getNonChar().values.length, 1); + Assert.assertEquals(subType.getNonChar().values[0].value, "abc"); + Assert.assertEquals(subType.getNonChar().allowed, true); + } + + @Test + public void testStringSubtypeSingleValue() { + ComplexSemType abc = (ComplexSemType) stringConst("abc"); + StringSubtype abcSD = (StringSubtype) abc.subtypeDataList()[0]; + Assert.assertEquals(StringSubtype.stringSubtypeSingleValue(abcSD).get(), "abc"); + + ComplexSemType a = (ComplexSemType) stringConst("a"); + StringSubtype aSD = (StringSubtype) a.subtypeDataList()[0]; + Assert.assertEquals(StringSubtype.stringSubtypeSingleValue(aSD).get(), "a"); + + ComplexSemType aAndAbc = (ComplexSemType) Core.union(a, abc); + Assert.assertEquals(StringSubtype.stringSubtypeSingleValue(aAndAbc.subtypeDataList()[0]), + Optional.empty()); + + ComplexSemType intersect1 = (ComplexSemType) Core.intersect(aAndAbc, a); + Assert.assertEquals(StringSubtype.stringSubtypeSingleValue(intersect1.subtypeDataList()[0]).get(), "a"); + ComplexSemType intersect2 = (ComplexSemType) Core.intersect(aAndAbc, abc); + Assert.assertEquals(StringSubtype.stringSubtypeSingleValue(intersect2.subtypeDataList()[0]).get(), "abc"); + SemType intersect3 = Core.intersect(a, abc); + Assert.assertEquals(intersect3.toString(), PredefinedType.NEVER.toString()); + } + + private static SemType createTupleType(Env env, SemType... members) { + ListDefinition ld = new ListDefinition(); + return ld.tupleTypeWrapped(env, members); + } +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeBitSet.java b/semtypes/src/test/java/io/ballerina/types/TypeTestUtils.java similarity index 50% rename from semtypes/src/main/java/io/ballerina/semtype/UniformTypeBitSet.java rename to semtypes/src/test/java/io/ballerina/types/TypeTestUtils.java index dc3621d7d0d3..09b3161140e4 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/UniformTypeBitSet.java +++ b/semtypes/src/test/java/io/ballerina/types/TypeTestUtils.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -15,17 +15,21 @@ * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype; -/** - * UniformTypeBitSet node. - * - * @since 2.0.0 - */ -public class UniformTypeBitSet implements SemType { - int bitset; +package io.ballerina.types; + +import io.ballerina.types.definition.ListDefinition; + +enum TypeTestUtils { + ; + + static SemType tuple(Env env, SemType... ty) { + ListDefinition ld = new ListDefinition(); + return ld.tupleTypeWrapped(env, ty); + } - public UniformTypeBitSet(int bitset) { - this.bitset = bitset; + static SemType roTuple(Env env, SemType... ty) { + ListDefinition ld = new ListDefinition(); + return ld.tupleTypeWrappedRo(env, ty); } } diff --git a/semtypes/src/test/resources/testng.xml b/semtypes/src/test/resources/testng.xml new file mode 100644 index 000000000000..018c612564da --- /dev/null +++ b/semtypes/src/test/resources/testng.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + diff --git a/settings.gradle b/settings.gradle index 21d05ef00350..1e0010e5f193 100644 --- a/settings.gradle +++ b/settings.gradle @@ -51,6 +51,7 @@ include(':ballerina-bindgen') include(':maven-resolver') include(':jballerina-unit-test') include(':jballerina-semtype-test') +include(':jballerina-semtype-port-test') include(':jballerina-benchmark-test') include(':ballerina-compiler-plugin-test') include(':ballerina-cli') @@ -216,6 +217,7 @@ project(':maven-resolver').projectDir = file('misc/maven-resolver') project(':jballerina-unit-test').projectDir = file('tests/jballerina-unit-test') project(':jballerina-semtype-test').projectDir = file('tests/jballerina-semtype-test') project(':jballerina-benchmark-test').projectDir = file('tests/jballerina-benchmark-test') +project(':jballerina-semtype-port-test').projectDir = file('tests/jballerina-semtype-port-test') project(':ballerina-compiler-plugin-test').projectDir = file('tests/ballerina-compiler-plugin-test') project(':central-client').projectDir = file('cli/central-client') project(':ballerina-cli').projectDir = file('cli/ballerina-cli') diff --git a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TypedescriptorTest.java b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TypedescriptorTest.java index 3ff673aa836f..e88942c82171 100644 --- a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TypedescriptorTest.java +++ b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TypedescriptorTest.java @@ -1128,10 +1128,10 @@ private Object[][] getSingletonType() { {353, 10, STRING, "\"abc\""}, {354, 8, FLOAT, "1.2"}, {355, 9, FLOAT, "3.4"}, - {356, 8, BYTE, "10"}, + {356, 8, INT, "10"}, {357, 11, INT, "46575"}, - {358, 12, FLOAT, "0xA1.B5p0"}, - {359, 14, FLOAT, "0xB2.8Fp1"}, + {358, 12, FLOAT, "161.70703125"}, + {359, 14, FLOAT, "357.1171875"}, {360, 8, STRING, "\"a\""}, {361, 8, STRING, "\"RED\""}, }; diff --git a/tests/ballerina-test-utils/build.gradle b/tests/ballerina-test-utils/build.gradle index 366be3b9ec98..9647982c1d06 100644 --- a/tests/ballerina-test-utils/build.gradle +++ b/tests/ballerina-test-utils/build.gradle @@ -22,6 +22,7 @@ plugins { dependencies { implementation project(':ballerina-tools-api') + implementation project(':ballerina-parser') implementation project(':ballerina-lang') implementation project(':ballerina-runtime') implementation project(':ballerina-cli') diff --git a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BCompileUtil.java b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BCompileUtil.java index ba140c78fc3f..3cf4bd961375 100644 --- a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BCompileUtil.java +++ b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/BCompileUtil.java @@ -17,9 +17,12 @@ */ package org.ballerinalang.test; +import io.ballerina.compiler.syntax.tree.SyntaxTree; import io.ballerina.projects.BuildOptions; +import io.ballerina.projects.DocumentId; import io.ballerina.projects.JBallerinaBackend; import io.ballerina.projects.JvmTarget; +import io.ballerina.projects.Module; import io.ballerina.projects.NullBackend; import io.ballerina.projects.Package; import io.ballerina.projects.PackageCompilation; @@ -36,6 +39,7 @@ import org.slf4j.LoggerFactory; import org.wso2.ballerinalang.compiler.bir.model.BIRNode; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BPackageSymbol; +import org.wso2.ballerinalang.compiler.tree.BLangPackage; import org.wso2.ballerinalang.programfile.CompiledBinaryFile; import java.io.IOException; @@ -118,6 +122,15 @@ public static CompileResult compileOffline(String sourceFilePath) { return compileResult; } + public static PackageSyntaxTreePair compileSemType(String sourceFilePath) { + Project project = loadProject(sourceFilePath); + Package currentPackage = project.currentPackage(); + Module module = currentPackage.getDefaultModule(); + DocumentId docId = module.documentIds().iterator().next(); + return new PackageSyntaxTreePair(currentPackage.getCompilation().defaultModuleBLangPackage(), + module.document(docId).syntaxTree()); + } + public static BIRCompileResult generateBIR(String sourceFilePath) { Project project = loadProject(sourceFilePath); NullBackend nullBackend = NullBackend.from(project.currentPackage().getCompilation()); @@ -323,4 +336,20 @@ public byte[] getActualBIR() { public static String getPlatformFromBala(String balaName, String packageName, String version) { return balaName.split(packageName + "-")[1].split("-" + version)[0]; } + + /** + * Contain compiled {@code BLangPackage} and Syntax tree. + * This result is used to test sem-type relationships. + * + * @since 3.0.0 + */ + public static class PackageSyntaxTreePair { + public final BLangPackage bLangPackage; + public final SyntaxTree syntaxTree; + + public PackageSyntaxTreePair(BLangPackage bLangPackage, SyntaxTree syntaxTree) { + this.bLangPackage = bLangPackage; + this.syntaxTree = syntaxTree; + } + } } diff --git a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/BServerInstance.java b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/BServerInstance.java index dcc709041ed0..91e5d95ac2fc 100644 --- a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/BServerInstance.java +++ b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/BServerInstance.java @@ -387,7 +387,7 @@ private void runBuildTool(String command, String[] args, Map env env.put(entry.getKey(), entry.getValue()); } } - process = processBuilder.start(); + Process process = processBuilder.start(); serverInfoLogReader = new ServerLogReader("inputStream", process.getInputStream()); tmpInfoLeechers.forEach(leecher -> serverInfoLogReader.addLeecher(leecher)); @@ -503,7 +503,7 @@ private void executeJarFile(String jarPath, String[] args, Map e for (Map.Entry entry : envProperties.entrySet()) { env.put(entry.getKey(), entry.getValue()); } - process = processBuilder.start(); + Process process = processBuilder.start(); serverInfoLogReader = new ServerLogReader("inputStream", process.getInputStream()); tmpInfoLeechers.forEach(leecher -> serverInfoLogReader.addLeecher(leecher)); diff --git a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/ServerLogReader.java b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/ServerLogReader.java index dd18161b5a1d..b9a1eb6399ac 100644 --- a/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/ServerLogReader.java +++ b/tests/ballerina-test-utils/src/main/java/org/ballerinalang/test/context/ServerLogReader.java @@ -55,7 +55,8 @@ public ServerLogReader(String name, InputStream inputStream) { * Start reading the stream. */ public void start() { - Thread.startVirtualThread(this); + Thread thread = new Thread(this); + thread.start(); } /** @@ -124,7 +125,7 @@ public void run() { } feedAndPrint(s); } else { - TimeUnit.MILLISECONDS.sleep(1); + TimeUnit.MICROSECONDS.sleep(1); } } String s = bufferedReader.readLine(); diff --git a/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/variables/VariableVisibilityTest.java b/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/variables/VariableVisibilityTest.java index ac8e2cedd79a..df0fdbaee897 100644 --- a/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/variables/VariableVisibilityTest.java +++ b/tests/jballerina-debugger-integration-test/src/test/java/org/ballerinalang/debugger/test/adapter/variables/VariableVisibilityTest.java @@ -35,6 +35,7 @@ import java.util.Map; import static org.ballerinalang.debugger.test.utils.DebugTestRunner.VariableScope; + /** * Test class for variable visibility. */ @@ -48,13 +49,14 @@ public class VariableVisibilityTest extends BaseTestCase { @Override @BeforeClass public void setup() { - String testProjectName = "variable-tests"; - String testModuleFileName = "main.bal"; - debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); } @Test(description = "Variable visibility test at the beginning(first line) of the main() method") public void initialVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 123)); debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); debugHitInfo = debugTestRunner.waitForDebugHit(25000); @@ -70,6 +72,10 @@ public void initialVariableVisibilityTest() throws BallerinaTestException { @Test(description = "Variable visibility test in the middle of the main() method for a new variable") public void newVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 245)); debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 316)); debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); @@ -105,6 +111,10 @@ public void newVariableVisibilityTest() throws BallerinaTestException { @Test(description = "Variable visibility test in control flows") public void controlFlowVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 266)); debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 270)); debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 277)); @@ -177,6 +187,10 @@ public void controlFlowVariableVisibilityTest() throws BallerinaTestException { @Test(description = "Variable visibility test for global variables") public void globalVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 352)); debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 327)); debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); @@ -215,6 +229,10 @@ public void globalVariableVisibilityTest() throws BallerinaTestException { @Test(description = "Variable visibility test for local variables at the last line of main() method") public void localVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 327)); debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 360)); debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); @@ -339,6 +357,10 @@ public void localVariableVisibilityTest() throws BallerinaTestException { @Test(enabled = false, description = "Child variable visibility test for local variables at the last line of main" + "() method") public void localVariableChildrenVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 327)); debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); debugHitInfo = debugTestRunner.waitForDebugHit(25000); @@ -352,18 +374,18 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // xml attributes child variable visibility test Map xmlAttributesChildVariables = - debugTestRunner.fetchChildVariables(xmlChildVariables.get("attributes")); + debugTestRunner.fetchChildVariables(xmlChildVariables.get("attributes")); debugTestRunner.assertVariable(xmlAttributesChildVariables, "gender", "\"male\"", "string"); // xml children variable visibility test Map xmlChildrenVariables = - debugTestRunner.fetchChildVariables(xmlChildVariables.get("children")); + debugTestRunner.fetchChildVariables(xmlChildVariables.get("children")); debugTestRunner.assertVariable(xmlChildrenVariables, "[0]", "XMLElement", "xml"); debugTestRunner.assertVariable(xmlChildrenVariables, "[1]", "XMLElement", "xml"); // xml grand children variable visibility test Map xmlGrandChildrenVariables = - debugTestRunner.fetchChildVariables(xmlChildrenVariables.get("[0]")); + debugTestRunner.fetchChildVariables(xmlChildrenVariables.get("[0]")); debugTestRunner.assertVariable(xmlGrandChildrenVariables, "children", "XMLSequence (size = 1)", "xml"); // array child variable visibility test @@ -391,7 +413,7 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // record child variable visibility test (Student record) Map studentRecordChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("recordVar")); + debugTestRunner.fetchChildVariables(localVariables.get("recordVar")); debugTestRunner.assertVariable(studentRecordChildVariables, "1st_name", "\"John Doe\"", "string"); debugTestRunner.assertVariable(studentRecordChildVariables, "grades", "Grades", "record"); debugTestRunner.assertVariable(studentRecordChildVariables, "Ȧɢέ_ /:@[`{~π", "20", "int"); @@ -399,7 +421,7 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // record child variable visibility test (Grades record) Map gradesChildVariables = - debugTestRunner.fetchChildVariables(studentRecordChildVariables.get("grades")); + debugTestRunner.fetchChildVariables(studentRecordChildVariables.get("grades")); debugTestRunner.assertVariable(gradesChildVariables, "chemistry", "65", "int"); debugTestRunner.assertVariable(gradesChildVariables, "maths", "80", "int"); debugTestRunner.assertVariable(gradesChildVariables, "physics", "75", "int"); @@ -407,7 +429,7 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // anonymous record child variable visibility test Map recordChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("anonRecord")); + debugTestRunner.fetchChildVariables(localVariables.get("anonRecord")); debugTestRunner.assertVariable(recordChildVariables, "city", "\"London\"", "string"); debugTestRunner.assertVariable(recordChildVariables, "country", "\"UK\"", "string"); @@ -418,18 +440,18 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // error details child variable visibility test Map errorDetailsChildVariables = - debugTestRunner.fetchChildVariables(errorChildVariables.get("details")); + debugTestRunner.fetchChildVariables(errorChildVariables.get("details")); debugTestRunner.assertVariable(errorDetailsChildVariables, "message", "\"Simple error occurred\"", "string"); // future child variable visibility test Map futureChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("futureVar")); + debugTestRunner.fetchChildVariables(localVariables.get("futureVar")); debugTestRunner.assertVariable(futureChildVariables, "isDone", "true", "boolean"); debugTestRunner.assertVariable(futureChildVariables, "result", "90", "int"); // object child variable visibility test (Person object) Map personObjectChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("objectVar")); + debugTestRunner.fetchChildVariables(localVariables.get("objectVar")); debugTestRunner.assertVariable(personObjectChildVariables, "1st_name", "\"John\"", "string"); debugTestRunner.assertVariable(personObjectChildVariables, "address", "\"No 20, Palm grove\"", "string"); debugTestRunner.assertVariable(personObjectChildVariables, "parent", "()", "nil"); @@ -438,7 +460,7 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // anonymous object child variable visibility test (AnonPerson object) Map anonObjectChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("anonObjectVar")); + debugTestRunner.fetchChildVariables(localVariables.get("anonObjectVar")); debugTestRunner.assertVariable(anonObjectChildVariables, "1st_name", "\"John\"", "string"); debugTestRunner.assertVariable(anonObjectChildVariables, "address", "\"No 20, Palm grove\"", "string"); debugTestRunner.assertVariable(anonObjectChildVariables, "parent", "()", "nil"); @@ -459,21 +481,21 @@ public void localVariableChildrenVisibilityTest() throws BallerinaTestException // table with key child variable visibility test Map tableWithKeyChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("tableWithKeyVar")); + debugTestRunner.fetchChildVariables(localVariables.get("tableWithKeyVar")); debugTestRunner.assertVariable(tableWithKeyChildVariables, "[0]", "Employee", "record"); debugTestRunner.assertVariable(tableWithKeyChildVariables, "[1]", "Employee", "record"); debugTestRunner.assertVariable(tableWithKeyChildVariables, "[2]", "Employee", "record"); // table without key child variable visibility test Map tableWithoutKeyChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("tableWithoutKeyVar")); + debugTestRunner.fetchChildVariables(localVariables.get("tableWithoutKeyVar")); debugTestRunner.assertVariable(tableWithoutKeyChildVariables, "[0]", "Employee", "record"); debugTestRunner.assertVariable(tableWithoutKeyChildVariables, "[1]", "Employee", "record"); debugTestRunner.assertVariable(tableWithoutKeyChildVariables, "[2]", "Employee", "record"); // service child variable visibility test Map serviceChildVariables = - debugTestRunner.fetchChildVariables(localVariables.get("serviceVar")); + debugTestRunner.fetchChildVariables(localVariables.get("serviceVar")); debugTestRunner.assertVariable(serviceChildVariables, "i", "5", "int"); } @@ -551,6 +573,68 @@ public void workerVariableVisibilityTest() throws BallerinaTestException { } } + @Test(description = "Binding pattern variables related visibility test") + public void bindingPatternVariableVisibilityTest() throws BallerinaTestException { + String testProjectName = "variable-tests-2"; + String testModuleFileName = "main.bal"; + debugTestRunner = new DebugTestRunner(testProjectName, testModuleFileName, true); + + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 35)); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 40)); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 43)); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 46)); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 49)); + debugTestRunner.addBreakPoint(new BallerinaTestDebugPoint(debugTestRunner.testEntryFilePath, 80)); + + debugTestRunner.initDebugSession(DebugUtils.DebuggeeExecutionKind.RUN); + Pair debugHitInfo = debugTestRunner.waitForDebugHit(25000); + + // simple binding pattern variables + localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); + debugTestRunner.assertVariable(localVariables, "profession", "\"Software Engineer\"", "string"); + + // list binding pattern variables + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + // TODO: enable after fixing runtime issue https://github.com/ballerina-platform/ballerina-lang/issues/43623 +// localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); +// debugTestRunner.assertVariable(localVariables, "id", "1234", "int"); +// debugTestRunner.assertVariable(localVariables, "firstName", "\"John Doe\"", "string"); + + // mapping binding pattern variables + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + // TODO: enable after fixing runtime issue https://github.com/ballerina-platform/ballerina-lang/issues/43623 +// localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); +// debugTestRunner.assertVariable(localVariables, "givenName", "\"Anne\"", "string"); +// debugTestRunner.assertVariable(localVariables, "surName", "\"Frank\"", "string"); + + // error binding pattern variables + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + // TODO: enable after fixing runtime issue https://github.com/ballerina-platform/ballerina-lang/issues/43623 +// localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); +// debugTestRunner.assertVariable(localVariables, "cause", "\"Database Error\"", "error"); +// debugTestRunner.assertVariable(localVariables, "code", "20", "int"); +// debugTestRunner.assertVariable(localVariables, "reason", "\"deadlock condition\"", "string"); + + // list binding pattern inside foreach statement + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); + debugTestRunner.assertVariable(localVariables, "name", "\"John\"", "string"); + debugTestRunner.assertVariable(localVariables, "age", "30", "int"); + + // list binding patterns inside match statement + debugTestRunner.resumeProgram(debugHitInfo.getRight(), DebugTestRunner.DebugResumeKind.NEXT_BREAKPOINT); + debugHitInfo = debugTestRunner.waitForDebugHit(10000); + // TODO: enable after fixing runtime issue https://github.com/ballerina-platform/ballerina-lang/issues/43623 +// localVariables = debugTestRunner.fetchVariables(debugHitInfo.getRight(), DebugTestRunner.VariableScope.LOCAL); +// debugTestRunner.assertVariable(localVariables, "remove", "Remove", "string"); +// debugTestRunner.assertVariable(localVariables, "all", "*", "string"); +// debugTestRunner.assertVariable(localVariables, "isDir", "true", "boolean"); + } + @Override @AfterMethod(alwaysRun = true) public void cleanUp() { diff --git a/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/Ballerina.toml b/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/Ballerina.toml new file mode 100644 index 000000000000..7352e4440b5f --- /dev/null +++ b/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/Ballerina.toml @@ -0,0 +1,4 @@ +[package] +org = "debug_test_resources" +name = "variable_tests_2" +version = "0.1.0" diff --git a/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/main.bal b/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/main.bal new file mode 100644 index 000000000000..12fefe845eb4 --- /dev/null +++ b/tests/jballerina-debugger-integration-test/src/test/resources/project-based-tests/variable-tests-2/main.bal @@ -0,0 +1,99 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// WSO2 LLC. licenses this file to you under the Apache License, +// Version 2.0 (the "License"); you may not use this file except +// in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +type Person record {| + int id; + string fname; + string lname; +|}; + +type SampleErrorData record {| + int code; + string reason; +|}; + +type SampleError error; + +public function main() { + // 1. simple binding pattern + var profession = "Software Engineer"; + + // 2. list binding pattern + [int, [string, string]] [id, [firstName, _]] = getDetails(); + + // 3. mapping binding pattern + string givenName; + string surname; + {fname: givenName, lname: surname} = getPerson(); + + // 4. error binding pattern + var error(_, cause, code = code, reason = reason) = getSampleError(); + + // 5. binding patterns inside a foreach statement + string names = ""; + [string, int][] personInfoList = getPersonInfo(); + foreach [string, int] [name, age] in personInfoList { + names += " " + name; + } + + // 6. binding patterns inside a match statement + matchCommand(["Remove", "*", true]); +} + +function getDetails() returns [int, [string, string]] { + return [ + 1234, + ["John", "Doe"] + ]; +} + +function getPerson() returns Person { + Person person = {id: 1001, fname: "Anne", lname: "Frank"}; + return person; +} + +function getSampleError() returns SampleError { + return error("Transaction Failure", error("Database Error"), code = 20, reason = "deadlock condition"); +} + +function matchCommand(any commands) { + match commands { + var [show] => { + string name = "show"; + } + // The list binding pattern below binds lists that contain three list items + // where the third element in the list is the boolean value `true`. + var [remove, all, isDir] if isDir is true => { + string name = "remove"; + } + // The list binding pattern below binds lists that contain three list items. + var [remove, all, _] => { + string name = "remove"; + } + // The list binding pattern below binds lists that contain two list items, + // in which the second list item is also a list of two items. + var [copy, [file1, file2]] => { + string name = "copy"; + } + _ => { + string name = "unknown"; + } + } +} + +function getPersonInfo() returns [string, int][] { + return [["John", 30]]; +} diff --git a/tests/jballerina-integration-test/src/test/resources/testng.xml b/tests/jballerina-integration-test/src/test/resources/testng.xml index 323d222cd163..dffc5fb44a00 100644 --- a/tests/jballerina-integration-test/src/test/resources/testng.xml +++ b/tests/jballerina-integration-test/src/test/resources/testng.xml @@ -20,7 +20,7 @@ - + diff --git a/tests/jballerina-semtype-port-test/build.gradle b/tests/jballerina-semtype-port-test/build.gradle new file mode 100644 index 000000000000..983e476c2b7d --- /dev/null +++ b/tests/jballerina-semtype-port-test/build.gradle @@ -0,0 +1,28 @@ +plugins { + id 'javaProject' + id 'ballerinaLangLibLoad' +} +dependencies { + implementation 'org.slf4j:slf4j-api' + implementation project(':ballerina-lang') + implementation project(':testerina:testerina-core') + implementation project(':ballerina-cli') + implementation project(':ballerina-lang:jballerina.java') + implementation project(':ballerina-lang-test') + implementation project(':ballerina-runtime') + implementation project(':docerina') + + testImplementation 'org.testng:testng' + testImplementation project(path: ':ballerina-test-utils', configuration: 'shadow') +} + +description = 'JBallerina Semtyp port - Unit Test Module' + +test { + // Add additional system property to distinguish tests requiring all basic types + systemProperty "ballerina.semtype.all.types.test", "true" + + useTestNG() { + suites 'src/test/resources/testng.xml' + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerSemTypeResolver.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerSemTypeResolver.java new file mode 100644 index 000000000000..ec8f4641d79b --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerSemTypeResolver.java @@ -0,0 +1,779 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.semtype.port.test; + +import io.ballerina.types.CellAtomicType; +import io.ballerina.types.Context; +import io.ballerina.types.Core; +import io.ballerina.types.Definition; +import io.ballerina.types.Env; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import io.ballerina.types.definition.Field; +import io.ballerina.types.definition.FunctionDefinition; +import io.ballerina.types.definition.FunctionQualifiers; +import io.ballerina.types.definition.ListDefinition; +import io.ballerina.types.definition.MappingDefinition; +import io.ballerina.types.definition.Member; +import io.ballerina.types.definition.ObjectDefinition; +import io.ballerina.types.definition.ObjectQualifiers; +import io.ballerina.types.definition.StreamDefinition; +import io.ballerina.types.subtypedata.FloatSubtype; +import io.ballerina.types.subtypedata.TableSubtype; +import org.ballerinalang.model.elements.Flag; +import org.ballerinalang.model.tree.IdentifierNode; +import org.ballerinalang.model.tree.NodeKind; +import org.ballerinalang.model.tree.types.ArrayTypeNode; +import org.ballerinalang.model.tree.types.TypeNode; +import org.ballerinalang.model.types.TypeKind; +import org.jetbrains.annotations.NotNull; +import org.wso2.ballerinalang.compiler.tree.BLangFunction; +import org.wso2.ballerinalang.compiler.tree.BLangNode; +import org.wso2.ballerinalang.compiler.tree.BLangResourceFunction; +import org.wso2.ballerinalang.compiler.tree.BLangSimpleVariable; +import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; +import org.wso2.ballerinalang.compiler.tree.BLangVariable; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangConstant; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangExpression; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.tree.types.BLangArrayType; +import org.wso2.ballerinalang.compiler.tree.types.BLangBuiltInRefTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangConstrainedType; +import org.wso2.ballerinalang.compiler.tree.types.BLangErrorType; +import org.wso2.ballerinalang.compiler.tree.types.BLangFiniteTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangFunctionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangIntersectionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangObjectTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangRecordTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangStreamType; +import org.wso2.ballerinalang.compiler.tree.types.BLangTableTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangTupleTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangType; +import org.wso2.ballerinalang.compiler.tree.types.BLangUnionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangUserDefinedType; +import org.wso2.ballerinalang.compiler.tree.types.BLangValueType; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Stream; + +import static org.ballerinalang.model.tree.NodeKind.CONSTANT; +import static org.wso2.ballerinalang.compiler.semantics.analyzer.SymbolEnter.getTypeOrClassName; + +/** + * Resolves sem-types for module definitions using compiler side semtype implementation. + * + * @since 2201.11.0 + */ +public class CompilerSemTypeResolver extends SemTypeResolver { + + private final Map attachedDefinitions = new HashMap<>(); + + public void defineSemTypes(List moduleDefs, TypeTestContext cx) { + Map modTable = new LinkedHashMap<>(); + for (BLangNode typeAndClassDef : moduleDefs) { + modTable.put(getTypeOrClassName(typeAndClassDef), typeAndClassDef); + } + modTable = Collections.unmodifiableMap(modTable); + + for (BLangNode def : moduleDefs) { + if (def.getKind() == NodeKind.CLASS_DEFN) { + throw new UnsupportedOperationException("Semtype are not supported for class definitions yet"); + } else if (def.getKind() == CONSTANT) { + resolveConstant(cx, modTable, (BLangConstant) def); + } else { + BLangTypeDefinition typeDefinition = (BLangTypeDefinition) def; + resolveTypeDefn(cx, modTable, typeDefinition, 0); + } + } + } + + @Override + protected void resolveConstant(TypeTestContext cx, Map modTable, + BLangConstant constant) { + SemType semtype = evaluateConst(constant); + addSemTypeBType(constant.getTypeNode(), semtype); + cx.getEnv().addTypeDef(constant.name.value, semtype); + } + + private SemType evaluateConst(BLangConstant constant) { + switch (constant.symbol.value.type.getKind()) { + case INT: + return SemTypes.intConst((long) constant.symbol.value.value); + case BOOLEAN: + return SemTypes.booleanConst((boolean) constant.symbol.value.value); + case STRING: + return SemTypes.stringConst((String) constant.symbol.value.value); + case FLOAT: + return SemTypes.floatConst((double) constant.symbol.value.value); + default: + throw new UnsupportedOperationException("Expression type not implemented for const semtype"); + } + } + + @Override + protected void resolveTypeDefn(TypeTestContext cx, Map mod, + BLangTypeDefinition defn) { + resolveTypeDefn(cx, mod, defn, 0); + } + + private SemType resolveTypeDefn(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth) { + if (defn.semType != null) { + return defn.semType; + } + + if (depth == defn.semCycleDepth) { + throw new IllegalStateException("cyclic type definition: " + defn.name.value); + } + defn.semCycleDepth = depth; + SemType s = resolveTypeDesc(cx, mod, defn, depth, defn.typeNode); + addSemTypeBType(defn.getTypeNode(), s); + if (defn.semType == null) { + defn.semType = s; + defn.semCycleDepth = -1; + cx.getEnv().addTypeDef(defn.name.value, s); + return s; + } else { + return s; + } + } + + private void addSemTypeBType(BLangType typeNode, SemType semType) { + if (typeNode != null) { + typeNode.getBType().semType(semType); + } + } + + public SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, TypeNode td) { + if (td == null) { + return null; + } + switch (td.getKind()) { + case VALUE_TYPE: + return resolveTypeDesc(cx, (BLangValueType) td); + case BUILT_IN_REF_TYPE: + return resolveTypeDesc(cx, (BLangBuiltInRefTypeNode) td); + case RECORD_TYPE: + return resolveTypeDesc(cx, (BLangRecordTypeNode) td, mod, depth, defn); + case CONSTRAINED_TYPE: // map and typedesc + return resolveTypeDesc(cx, (BLangConstrainedType) td, mod, depth, defn); + case UNION_TYPE_NODE: + return resolveTypeDesc(cx, (BLangUnionTypeNode) td, mod, depth, defn); + case INTERSECTION_TYPE_NODE: + return resolveTypeDesc(cx, (BLangIntersectionTypeNode) td, mod, depth, defn); + case USER_DEFINED_TYPE: + return resolveTypeDesc(cx, (BLangUserDefinedType) td, mod, depth); + case FINITE_TYPE_NODE: + return resolveSingletonType((BLangFiniteTypeNode) td); + case ARRAY_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangArrayType) td); + case TUPLE_TYPE_NODE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangTupleTypeNode) td); + case FUNCTION_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangFunctionTypeNode) td); + case TABLE_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangTableTypeNode) td); + case ERROR_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangErrorType) td); + case OBJECT_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangObjectTypeNode) td); + case STREAM_TYPE: + return resolveTypeDesc(cx, mod, defn, depth, (BLangStreamType) td); + default: + throw new UnsupportedOperationException("type not implemented: " + td.getKind()); + } + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangObjectTypeNode td) { + SemType innerType = resolveNonDistinctObject(cx, mod, defn, depth, td); + if (td.flagSet.contains(Flag.DISTINCT)) { + return getDistinctObjectType((Env) cx.getInnerEnv(), innerType); + } + return innerType; + } + + private static SemType getDistinctObjectType(Env env, SemType innerType) { + return Core.intersect(SemTypes.objectDistinct(env.distinctAtomCountGetAndIncrement()), innerType); + } + + private SemType resolveNonDistinctObject(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, + int depth, BLangObjectTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + if (td.defn != null) { + return td.defn.getSemType(env); + } + ObjectDefinition od = new ObjectDefinition(); + Stream fieldStream = td.fields.stream().map(field -> { + Set flags = field.flagSet; + Member.Visibility visibility = flags.contains(Flag.PUBLIC) ? Member.Visibility.Public : + Member.Visibility.Private; + SemType ty = resolveTypeDesc(cx, mod, defn, depth + 1, field.typeNode); + return new Member(field.name.value, ty, Member.Kind.Field, visibility, flags.contains(Flag.READONLY)); + }); + Stream methodStream = td.getFunctions().stream().map(method -> { + Member.Visibility visibility = method.flagSet.contains(Flag.PUBLIC) ? Member.Visibility.Public : + Member.Visibility.Private; + SemType ty = resolveTypeDesc(cx, mod, defn, depth + 1, method); + return new Member(method.name.value, ty, Member.Kind.Method, visibility, true); + }); + td.defn = od; + List members = Stream.concat(fieldStream, methodStream).toList(); + ObjectQualifiers qualifiers = getQualifiers(td); + return od.define(env, qualifiers, members); + } + + private static ObjectQualifiers getQualifiers(BLangObjectTypeNode td) { + Set flags = td.symbol.getFlags(); + ObjectQualifiers.NetworkQualifier networkQualifier; + assert !(flags.contains(Flag.CLIENT) && flags.contains(Flag.SERVICE)) : + "object can't be both client and service"; + if (flags.contains(Flag.CLIENT)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Client; + } else if (flags.contains(Flag.SERVICE)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Service; + } else { + networkQualifier = ObjectQualifiers.NetworkQualifier.None; + } + return new ObjectQualifiers(flags.contains(Flag.ISOLATED), flags.contains(Flag.READONLY), networkQualifier); + } + + // TODO: should we make definition part of BLangFunction as well? + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangFunction functionType) { + Definition attached = attachedDefinitions.get(functionType); + Env env = (Env) cx.getInnerEnv(); + if (attached != null) { + return attached.getSemType(env); + } + FunctionDefinition fd = new FunctionDefinition(); + attachedDefinitions.put(functionType, fd); + Map paramScope = new HashMap<>(); + List params = getParameters(cx, mod, paramScope, defn, depth, functionType); + SemType rest; + if (functionType.getRestParameters() == null) { + rest = PredefinedType.NEVER; + } else { + ArrayTypeNode arrayType = (ArrayTypeNode) functionType.getRestParameters().getTypeNode(); + rest = resolveTypeDesc(cx, mod, defn, depth + 1, arrayType.getElementType()); + } + SemType returnType = resolveReturnType(cx, mod, paramScope, defn, depth + 1, functionType.getReturnTypeNode()); + ListDefinition paramListDefinition = new ListDefinition(); + FunctionQualifiers qualifiers = FunctionQualifiers.from(env, functionType.flagSet.contains(Flag.ISOLATED), + functionType.flagSet.contains(Flag.TRANSACTIONAL)); + return fd.define(env, paramListDefinition.defineListTypeWrapped(env, params, params.size(), rest, + CellAtomicType.CellMutability.CELL_MUT_NONE), returnType, qualifiers); + } + + @NotNull + private List getParameters(TypeTestContext cx, Map mod, + Map paramScope, BLangTypeDefinition defn, int depth, + BLangFunction functionType) { + List params = new ArrayList<>(); + if (functionType instanceof BLangResourceFunction resourceFunctionType) { + params.add(SemTypes.stringConst(resourceFunctionType.methodName.value)); + for (var each : resourceFunctionType.resourcePathSegments) { + params.add(resolveTypeDesc(cx, mod, defn, depth + 1, each.typeNode)); + } + } + for (BLangSimpleVariable paramVar : functionType.getParameters()) { + SemType semType = resolveTypeDesc(cx, mod, defn, depth + 1, paramVar.typeNode); + if (Core.isSubtypeSimple(semType, PredefinedType.TYPEDESC)) { + paramScope.put(paramVar.name.value, paramVar); + } + params.add(semType); + } + return params; + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangFunctionTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + if (isFunctionTop(td)) { + if (td.flagSet.contains(Flag.ISOLATED) || td.flagSet.contains(Flag.TRANSACTIONAL)) { + FunctionQualifiers qualifiers = FunctionQualifiers.from(env, td.flagSet.contains(Flag.ISOLATED), + td.flagSet.contains(Flag.TRANSACTIONAL)); + // I think param type here is wrong. It should be the intersection of all list types, but I think + // never is close enough + return new FunctionDefinition().define(env, PredefinedType.NEVER, PredefinedType.VAL, qualifiers); + } + return PredefinedType.FUNCTION; + } + if (td.defn != null) { + return td.defn.getSemType((Env) cx.getInnerEnv()); + } + FunctionDefinition fd = new FunctionDefinition(); + td.defn = fd; + Map tdScope = new HashMap<>(); + List params = new ArrayList<>(td.params.size()); + for (BLangSimpleVariable param : td.params) { + SemType paramType = resolveTypeDesc(cx, mod, defn, depth + 1, param.typeNode); + params.add(paramType); + if (Core.isSubtypeSimple(paramType, PredefinedType.TYPEDESC)) { + tdScope.put(param.name.value, param); + } + } + SemType rest; + if (td.restParam == null) { + rest = PredefinedType.NEVER; + } else { + BLangArrayType restArrayType = (BLangArrayType) td.restParam.typeNode; + rest = resolveTypeDesc(cx, mod, defn, depth + 1, restArrayType.elemtype); + } + SemType returnType = resolveReturnType(cx, mod, tdScope, defn, depth + 1, td.returnTypeNode); + ListDefinition paramListDefinition = new ListDefinition(); + FunctionQualifiers qualifiers = FunctionQualifiers.from(env, td.flagSet.contains(Flag.ISOLATED), + td.flagSet.contains(Flag.TRANSACTIONAL)); + return fd.define(env, paramListDefinition.defineListTypeWrapped(env, params, params.size(), rest, + CellAtomicType.CellMutability.CELL_MUT_NONE), returnType, qualifiers); + } + + private SemType resolveReturnType(TypeTestContext cx, Map mod, + Map mayBeDependentlyTypeNodes, BLangTypeDefinition defn, + int depth, BLangType returnTypeNode) { + if (returnTypeNode == null) { + return PredefinedType.NIL; + } + SemType innerType; + // Dependently typed function are quite rare so doing it via exception handling should be faster than actually + // checking if it is a dependently typed one. + boolean isDependentlyType; + try { + innerType = resolveTypeDesc(cx, mod, defn, depth + 1, returnTypeNode); + isDependentlyType = false; + } catch (IndexOutOfBoundsException err) { + innerType = + resolveDependentlyTypedReturnType(cx, mod, mayBeDependentlyTypeNodes, defn, depth, returnTypeNode); + isDependentlyType = true; + } + ListDefinition ld = new ListDefinition(); + return ld.tupleTypeWrapped((Env) cx.getInnerEnv(), + !isDependentlyType ? PredefinedType.BOOLEAN : SemTypes.booleanConst(true), innerType); + } + + private SemType resolveDependentlyTypedReturnType(TypeTestContext cx, Map mod, + Map mayBeDependentlyTypeNodes, + BLangTypeDefinition defn, int depth, + TypeNode returnTypeNode) { + Map combined = new HashMap<>(mod); + combined.putAll(mayBeDependentlyTypeNodes); + return resolveTypeDesc(cx, combined, defn, depth + 1, returnTypeNode); + } + + private boolean isFunctionTop(BLangFunctionTypeNode td) { + return td.params.isEmpty() && td.restParam == null && td.returnTypeNode == null; + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangArrayType td) { + if (td.defn != null) { + return td.defn.getSemType((Env) cx.getInnerEnv()); + } + ListDefinition ld = new ListDefinition(); + td.defn = ld; + + int dimensions = td.dimensions; + SemType accum = resolveTypeDesc(cx, mod, defn, depth + 1, td.elemtype); + for (int i = 0; i < dimensions; i++) { + int size = from(mod, td.sizes.get(i)); + if (i == dimensions - 1) { + accum = resolveListInner(cx, ld, size, accum); + } else { + accum = resolveListInner(cx, size, accum); + } + } + return accum; + } + + private SemType resolveListInner(TypeTestContext cx, int size, SemType eType) { + ListDefinition ld = new ListDefinition(); + return resolveListInner(cx, ld, size, eType); + } + + private static SemType resolveListInner(TypeTestContext cx, ListDefinition ld, int size, SemType eType) { + Env env = (Env) cx.getInnerEnv(); + if (size != -1) { + return ld.defineListTypeWrapped(env, List.of(eType), Math.abs(size), PredefinedType.NEVER, + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + } else { + return ld.defineListTypeWrapped(env, List.of(), 0, eType, + CellAtomicType.CellMutability.CELL_MUT_LIMITED); + } + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, + BLangTupleTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + if (td.defn != null) { + return td.defn.getSemType(env); + } + ListDefinition ld = new ListDefinition(); + td.defn = ld; + List memberSemTypes = + td.members.stream().map(member -> resolveTypeDesc(cx, mod, defn, depth + 1, member.typeNode)) + .toList(); + SemType rest = td.restParamType != null ? resolveTypeDesc(cx, mod, defn, depth + 1, td.restParamType) : + PredefinedType.NEVER; + return ld.defineListTypeWrapped(env, memberSemTypes, memberSemTypes.size(), rest); + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) { + Context innerContext = (Context) cx.getInnerContext(); + switch (td.typeKind) { + case NIL: + return PredefinedType.NIL; + case BOOLEAN: + return PredefinedType.BOOLEAN; + case BYTE: + return PredefinedType.BYTE; + case INT: + return PredefinedType.INT; + case FLOAT: + return PredefinedType.FLOAT; + case DECIMAL: + return PredefinedType.DECIMAL; + case STRING: + return PredefinedType.STRING; + case TYPEDESC: + return PredefinedType.TYPEDESC; + case ERROR: + return PredefinedType.ERROR; + case HANDLE: + return PredefinedType.HANDLE; + case XML: + return PredefinedType.XML; + case ANY: + return PredefinedType.ANY; + case READONLY: + return PredefinedType.VAL_READONLY; + case ANYDATA: + return Core.createAnydata(innerContext); + case JSON: + return Core.createJson(innerContext); + default: + throw new IllegalStateException("Unknown type: " + td); + } + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangBuiltInRefTypeNode td) { + return switch (td.typeKind) { + case NEVER -> PredefinedType.NEVER; + case XML -> PredefinedType.XML; + case JSON -> Core.createJson((Context) cx.getInnerContext()); + case FUTURE -> PredefinedType.FUTURE; + default -> throw new UnsupportedOperationException("Built-in ref type not implemented: " + td.typeKind); + }; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangConstrainedType td, Map mod, + int depth, BLangTypeDefinition defn) { + TypeKind typeKind = ((BLangBuiltInRefTypeNode) td.getType()).getTypeKind(); + return switch (typeKind) { + case MAP -> resolveMapTypeDesc(td, cx, mod, depth, defn); + case XML -> resolveXmlTypeDesc(td, cx, mod, depth, defn); + case FUTURE -> resolveFutureTypeDesc(td, cx, mod, depth, defn); + case TYPEDESC -> resolveTypedescTypeDesc(td, cx, mod, depth, defn); + default -> throw new IllegalStateException("Unexpected constrained type: " + typeKind); + }; + } + + private SemType resolveMapTypeDesc(BLangConstrainedType td, TypeTestContext cx, Map mod, + int depth, BLangTypeDefinition typeDefinition) { + Env env = (Env) cx.getInnerEnv(); + if (td.defn != null) { + return td.defn.getSemType(env); + } + + MappingDefinition d = new MappingDefinition(); + td.defn = d; + + SemType rest = resolveTypeDesc(cx, mod, typeDefinition, depth + 1, td.constraint); + return d.defineMappingTypeWrapped(env, Collections.emptyList(), rest == null ? PredefinedType.NEVER : rest); + } + + private SemType resolveXmlTypeDesc(BLangConstrainedType td, TypeTestContext cx, Map mod, + int depth, + BLangTypeDefinition defn) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return SemTypes.xmlSequence(constraint); + } + + private SemType resolveFutureTypeDesc(BLangConstrainedType td, TypeTestContext cx, + Map mod, int depth, + BLangTypeDefinition defn) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return SemTypes.futureContaining((Env) cx.getInnerEnv(), constraint); + } + + private SemType resolveTypedescTypeDesc(BLangConstrainedType td, TypeTestContext cx, + Map mod, int depth, + BLangTypeDefinition defn) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return SemTypes.typedescContaining((Env) cx.getInnerEnv(), constraint); + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangRecordTypeNode td, Map mod, + int depth, BLangTypeDefinition typeDefinition) { + if (td.defn != null) { + return td.defn.getSemType((Env) cx.getInnerEnv()); + } + + MappingDefinition d = new MappingDefinition(); + td.defn = d; + + List fields = new ArrayList<>(); + for (BLangSimpleVariable field : td.fields) { + SemType ty = resolveTypeDesc(cx, mod, typeDefinition, depth + 1, field.typeNode); + if (Core.isNever(ty)) { + throw new IllegalStateException("record field can't be never"); + } + fields.add(Field.from(field.name.value, ty, false, field.flagSet.contains(Flag.OPTIONAL))); + } + + SemType rest; + if (!td.isSealed() && td.getRestFieldType() == null) { + rest = Core.createAnydata((Context) cx.getInnerContext()); + } else { + rest = resolveTypeDesc(cx, mod, typeDefinition, depth + 1, td.restFieldType); + } + + return d.defineMappingTypeWrapped((Env) cx.getInnerEnv(), fields, rest == null ? PredefinedType.NEVER : rest); + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangUnionTypeNode td, Map mod, + int depth, + BLangTypeDefinition defn) { + Iterator iterator = td.memberTypeNodes.iterator(); + SemType u = resolveTypeDesc(cx, mod, defn, depth, iterator.next()); + while (iterator.hasNext()) { + u = Core.union(u, resolveTypeDesc(cx, mod, defn, depth, iterator.next())); + } + return u; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangIntersectionTypeNode td, + Map mod, int depth, + BLangTypeDefinition defn) { + Iterator iterator = td.constituentTypeNodes.iterator(); + SemType i = resolveTypeDesc(cx, mod, defn, depth, iterator.next()); + while (iterator.hasNext()) { + i = Core.intersect(i, resolveTypeDesc(cx, mod, defn, depth, iterator.next())); + } + return i; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedType td, Map mod, + int depth) { + String name = td.typeName.value; + // Need to replace this with a real package lookup + if (td.pkgAlias.value.equals("int")) { + return resolveIntSubtype(name); + } else if (td.pkgAlias.value.equals("string") && name.equals("Char")) { + return SemTypes.CHAR; + } else if (td.pkgAlias.value.equals("xml")) { + return resolveXmlSubtype(name); + } else if (td.pkgAlias.value.equals("regexp") && name.equals("RegExp")) { + return PredefinedType.REGEXP; + } + + BLangNode moduleLevelDef = mod.get(name); + if (moduleLevelDef == null) { + throw new IndexOutOfBoundsException("unknown type " + name); + } + + switch (moduleLevelDef.getKind()) { + case TYPE_DEFINITION -> { + SemType ty = resolveTypeDefn(cx, mod, (BLangTypeDefinition) moduleLevelDef, depth); + if (td.flagSet.contains(Flag.DISTINCT)) { + return getDistinctSemType(cx, ty); + } + return ty; + } + case CONSTANT -> { + BLangConstant constant = (BLangConstant) moduleLevelDef; + return resolveTypeDefn(cx, mod, constant.getAssociatedTypeDefinition(), depth); + } + case VARIABLE -> { + // This happens when the type is a parameter of a dependently typed function + BLangVariable variable = (BLangVariable) moduleLevelDef; + BLangConstrainedType typeDescType = (BLangConstrainedType) variable.getTypeNode(); + return resolveTypeDesc(cx, mod, null, depth, typeDescType.constraint); + } + default -> throw new UnsupportedOperationException("class defns not implemented"); + } + } + + private SemType getDistinctSemType(TypeTestContext cx, SemType innerType) { + Env env = (Env) cx.getInnerEnv(); + if (Core.isSubtypeSimple(innerType, PredefinedType.OBJECT)) { + return CompilerSemTypeResolver.getDistinctObjectType(env, innerType); + } else if (Core.isSubtypeSimple(innerType, PredefinedType.ERROR)) { + return getDistinctErrorType(env, innerType); + } + throw new IllegalArgumentException("Distinct type not supported for: " + innerType); + } + + private SemType resolveIntSubtype(String name) { + // TODO: support MAX_VALUE + return switch (name) { + case "Signed8" -> SemTypes.SINT8; + case "Signed16" -> SemTypes.SINT16; + case "Signed32" -> SemTypes.SINT32; + case "Unsigned8" -> SemTypes.UINT8; + case "Unsigned16" -> SemTypes.UINT16; + case "Unsigned32" -> SemTypes.UINT32; + default -> throw new UnsupportedOperationException("Unknown int subtype: " + name); + }; + } + + private SemType resolveXmlSubtype(String name) { + return switch (name) { + case "Element" -> SemTypes.XML_ELEMENT; + case "Comment" -> SemTypes.XML_COMMENT; + case "Text" -> SemTypes.XML_TEXT; + case "ProcessingInstruction" -> SemTypes.XML_PI; + default -> throw new IllegalStateException("Unknown XML subtype: " + name); + }; + } + + private SemType resolveSingletonType(BLangFiniteTypeNode td) { + return resolveSingletonType(td.valueSpace); + } + + private SemType resolveSingletonType(List valueSpace) { + List types = new ArrayList<>(); + for (BLangExpression bLangExpression : valueSpace) { + types.add(resolveSingletonType((BLangLiteral) bLangExpression)); + } + + Iterator iter = types.iterator(); + SemType u = iter.next(); + while (iter.hasNext()) { + u = SemTypes.union(u, iter.next()); + } + return u; + } + + private SemType resolveSingletonType(BLangLiteral literal) { + return resolveSingletonType(literal.value, literal.getDeterminedType().getKind()); + } + + private SemType resolveSingletonType(Object value, TypeKind targetTypeKind) { + return switch (targetTypeKind) { + case NIL -> PredefinedType.NIL; + case BOOLEAN -> SemTypes.booleanConst((Boolean) value); + case INT, BYTE -> { + assert !(value instanceof Byte); + yield SemTypes.intConst(((Number) value).longValue()); + } + case FLOAT -> { + double doubleVal; + if (value instanceof Long longValue) { + doubleVal = longValue.doubleValue(); + } else if (value instanceof Double doubleValue) { + doubleVal = doubleValue; + } else { + // literal value will be a string if it wasn't within the bounds of what is supported by Java Long + // or Double when it was parsed in BLangNodeBuilder. + try { + doubleVal = Double.parseDouble((String) value); + } catch (NumberFormatException e) { + // We reach here when there is a syntax error. Mock the flow with default float value. + yield FloatSubtype.floatConst(0); + } + } + yield SemTypes.floatConst(doubleVal); + // literal value will be a string if it wasn't within the bounds of what is supported by Java Long + // or Double when it was parsed in BLangNodeBuilder. + // We reach here when there is a syntax error. Mock the flow with default float value. + } + case DECIMAL -> SemTypes.decimalConst((String) value); + case STRING -> SemTypes.stringConst((String) value); + default -> throw new UnsupportedOperationException("Finite type not implemented for: " + targetTypeKind); + }; + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangTableTypeNode td) { + SemType tableConstraint = resolveTypeDesc(cx, mod, defn, depth, td.constraint); + Context context = (Context) cx.getInnerContext(); + if (td.tableKeySpecifier != null) { + List fieldNameIdentifierList = td.tableKeySpecifier.fieldNameIdentifierList; + String[] fieldNames = fieldNameIdentifierList.stream().map(IdentifierNode::getValue).toArray(String[]::new); + return TableSubtype.tableContainingKeySpecifier(context, tableConstraint, fieldNames); + } + + if (td.tableKeyTypeConstraint != null) { + SemType keyConstraint = resolveTypeDesc(cx, mod, defn, depth, td.tableKeyTypeConstraint.keyType); + return TableSubtype.tableContainingKeyConstraint(context, tableConstraint, keyConstraint); + } + + return TableSubtype.tableContaining(context.env, tableConstraint); + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, + BLangErrorType td) { + SemType err; + if (td.detailType == null) { + err = PredefinedType.ERROR; + } else { + SemType detail = resolveTypeDesc(cx, mod, defn, depth, td.detailType); + err = SemTypes.errorDetail(detail); + } + + if (td.flagSet.contains(Flag.DISTINCT)) { + Env env = (Env) cx.getInnerEnv(); + err = getDistinctErrorType(env, err); + } + return err; + } + + private static SemType getDistinctErrorType(Env env, SemType err) { + return Core.intersect(SemTypes.errorDistinct(env.distinctAtomCountGetAndIncrement()), err); + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangStreamType td) { + if (td.constraint == null) { + return PredefinedType.STREAM; + } + Env env = (Env) cx.getInnerEnv(); + if (td.defn != null) { + return td.defn.getSemType(env); + } + StreamDefinition d = new StreamDefinition(); + td.defn = d; + + SemType valueType = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + SemType completionType = td.error == null ? + PredefinedType.NIL : resolveTypeDesc(cx, mod, defn, depth + 1, td.error); + return d.define(env, valueType, completionType); + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestAPI.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestAPI.java new file mode 100644 index 000000000000..71870e480870 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestAPI.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.types.BasicTypeBitSet; +import io.ballerina.types.Context; +import io.ballerina.types.PredefinedType; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; + +public final class CompilerTypeTestAPI implements TypeTestAPI { + + private static final CompilerTypeTestAPI INSTANCE = new CompilerTypeTestAPI(); + + private CompilerTypeTestAPI() { + } + + public static CompilerTypeTestAPI getInstance() { + return INSTANCE; + } + + @Override + public boolean isSubtype(TypeTestContext cx, SemType t1, SemType t2) { + return SemTypes.isSubtype(form(cx), t1, t2); + } + + private static Context form(TypeTestContext cx) { + return (Context) cx.getInnerContext(); + } + + @Override + public boolean isSubtypeSimple(SemType t1, SemType t2) { + return SemTypes.isSubtypeSimple(t1, (BasicTypeBitSet) t2); + } + + @Override + public boolean isListType(SemType t) { + return SemTypes.isSubtypeSimple(t, PredefinedType.LIST); + } + + @Override + public boolean isMapType(SemType t) { + return SemTypes.isSubtypeSimple(t, PredefinedType.MAPPING); + } + + @Override + public SemType intConst(long l) { + return SemTypes.intConst(l); + } + + @Override + public SemType mappingMemberTypeInnerVal(TypeTestContext context, SemType semType, SemType m) { + return SemTypes.mappingMemberTypeInnerVal((Context) context.getInnerContext(), semType, m); + } + + @Override + public SemType listProj(TypeTestContext context, SemType t, SemType key) { + return SemTypes.listProj((Context) context.getInnerContext(), t, key); + } + + @Override + public SemType listMemberType(TypeTestContext context, SemType t, SemType key) { + return SemTypes.listMemberType((Context) context.getInnerContext(), t, key); + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestEnv.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestEnv.java new file mode 100644 index 000000000000..116d114445be --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/CompilerTypeTestEnv.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.types.Env; +import io.ballerina.types.SemType; + +import java.util.Map; + +public class CompilerTypeTestEnv implements TypeTestEnv { + + private final Env env; + + private CompilerTypeTestEnv(Env env) { + this.env = env; + } + + public static synchronized CompilerTypeTestEnv from(Env env) { + return new CompilerTypeTestEnv(env); + } + + @Override + public Map getTypeNameSemTypeMap() { + return env.getTypeNameSemTypeMap(); + } + + @Override + public void addTypeDef(String value, SemType semtype) { + env.addTypeDef(value, semtype); + } + + @Override + public Object getInnerEnv() { + return env; + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/ComplierTypeTestContext.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/ComplierTypeTestContext.java new file mode 100644 index 000000000000..ceafd5eddba1 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/ComplierTypeTestContext.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.types.Context; +import io.ballerina.types.SemType; + +public class ComplierTypeTestContext implements TypeTestContext { + + private final Context context; + private final TypeTestEnv env; + + private ComplierTypeTestContext(Context context) { + this.context = context; + env = CompilerTypeTestEnv.from(context.env); + } + + public static synchronized ComplierTypeTestContext from(Context context) { + return new ComplierTypeTestContext(context); + } + + @Override + public TypeTestEnv getEnv() { + return env; + } + + @Override + public Object getInnerEnv() { + return context.env; + } + + @Override + public Object getInnerContext() { + return context; + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeSemTypeResolver.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeSemTypeResolver.java new file mode 100644 index 000000000000..14644cc3e289 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeSemTypeResolver.java @@ -0,0 +1,738 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +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.Definition; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; +import io.ballerina.runtime.internal.types.semtype.CellAtomicType; +import io.ballerina.runtime.internal.types.semtype.ErrorUtils; +import io.ballerina.runtime.internal.types.semtype.FunctionDefinition; +import io.ballerina.runtime.internal.types.semtype.FunctionQualifiers; +import io.ballerina.runtime.internal.types.semtype.FutureUtils; +import io.ballerina.runtime.internal.types.semtype.ListDefinition; +import io.ballerina.runtime.internal.types.semtype.MappingDefinition; +import io.ballerina.runtime.internal.types.semtype.Member; +import io.ballerina.runtime.internal.types.semtype.ObjectDefinition; +import io.ballerina.runtime.internal.types.semtype.ObjectQualifiers; +import io.ballerina.runtime.internal.types.semtype.StreamDefinition; +import io.ballerina.runtime.internal.types.semtype.TableUtils; +import io.ballerina.runtime.internal.types.semtype.TypedescUtils; +import io.ballerina.runtime.internal.types.semtype.XmlUtils; +import org.ballerinalang.model.elements.Flag; +import org.ballerinalang.model.tree.IdentifierNode; +import org.ballerinalang.model.tree.types.ArrayTypeNode; +import org.ballerinalang.model.tree.types.TypeNode; +import org.ballerinalang.model.types.TypeKind; +import org.wso2.ballerinalang.compiler.tree.BLangFunction; +import org.wso2.ballerinalang.compiler.tree.BLangNode; +import org.wso2.ballerinalang.compiler.tree.BLangResourceFunction; +import org.wso2.ballerinalang.compiler.tree.BLangSimpleVariable; +import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; +import org.wso2.ballerinalang.compiler.tree.BLangVariable; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangConstant; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.tree.types.BLangArrayType; +import org.wso2.ballerinalang.compiler.tree.types.BLangBuiltInRefTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangConstrainedType; +import org.wso2.ballerinalang.compiler.tree.types.BLangErrorType; +import org.wso2.ballerinalang.compiler.tree.types.BLangFiniteTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangFunctionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangIntersectionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangObjectTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangRecordTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangStreamType; +import org.wso2.ballerinalang.compiler.tree.types.BLangTableTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangTupleTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangType; +import org.wso2.ballerinalang.compiler.tree.types.BLangUnionTypeNode; +import org.wso2.ballerinalang.compiler.tree.types.BLangUserDefinedType; +import org.wso2.ballerinalang.compiler.tree.types.BLangValueType; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED16_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED16_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED32_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED32_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED8_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.SIGNED8_MIN_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED16_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED32_MAX_VALUE; +import static io.ballerina.runtime.api.constants.RuntimeConstants.UNSIGNED8_MAX_VALUE; +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; + +/** + * Resolves sem-types for module definitions using runtime side semtype implementation. + * + * @since 2201.11.0 + */ +class RuntimeSemTypeResolver extends SemTypeResolver { + + private static final SemType[] EMPTY_SEMTYPE_ARR = {}; + Map attachedSemType = new HashMap<>(); + Map semTypeMemo = new HashMap<>(); + Map attachedDefinitions = new HashMap<>(); + + @Override + public void resolveTypeDefn(TypeTestContext cx, Map modTable, + BLangTypeDefinition typeDefinition) { + resolveTypeDefnRec(cx, modTable, typeDefinition, 0); + } + + @Override + public void resolveConstant(TypeTestContext cx, Map modTable, BLangConstant constant) { + SemType semtype = evaluateConst(constant); + attachToBType(constant.typeNode, semtype); + cx.getEnv().addTypeDef(constant.name.value, semtype); + } + + private SemType resolveTypeDefnRec(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth) { + SemType memo = semTypeMemo.get(defn); + if (memo != null) { + return memo; + } + if (depth == defn.semCycleDepth) { + throw new IllegalStateException("cyclic type definition: " + defn.name.value); + } + + defn.semCycleDepth = depth; + SemType s = resolveTypeDesc(cx, mod, defn, depth, defn.typeNode); + attachToBType(defn.getTypeNode(), s); + if (!semTypeMemo.containsKey(defn)) { + semTypeMemo.put(defn, s); + defn.semCycleDepth = -1; + cx.getEnv().addTypeDef(defn.name.value, s); + return s; + } else { + return s; + } + } + + private SemType resolveTypeDesc(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, TypeNode td) { + if (td == null) { + return null; + } + return switch (td.getKind()) { + case VALUE_TYPE -> resolveTypeDesc(cx, (BLangValueType) td); + case BUILT_IN_REF_TYPE -> resolveTypeDesc((BLangBuiltInRefTypeNode) td); + case INTERSECTION_TYPE_NODE -> resolveTypeDesc(cx, (BLangIntersectionTypeNode) td, mod, depth, defn); + case UNION_TYPE_NODE -> resolveTypeDesc(cx, (BLangUnionTypeNode) td, mod, depth, defn); + case USER_DEFINED_TYPE -> resolveTypeDesc(cx, (BLangUserDefinedType) td, mod, depth); + case FINITE_TYPE_NODE -> resolveSingletonType((BLangFiniteTypeNode) td); + case ARRAY_TYPE -> resolveArrayTypeDesc(cx, mod, defn, depth, (BLangArrayType) td); + case TUPLE_TYPE_NODE -> resolveTupleTypeDesc(cx, mod, defn, depth, (BLangTupleTypeNode) td); + case CONSTRAINED_TYPE -> resolveConstrainedTypeDesc(cx, mod, defn, depth, (BLangConstrainedType) td); + case RECORD_TYPE -> resolveRecordTypeDesc(cx, mod, defn, depth, (BLangRecordTypeNode) td); + case FUNCTION_TYPE -> resolveFunctionTypeDesc(cx, mod, defn, depth, (BLangFunctionTypeNode) td); + case OBJECT_TYPE -> resolveObjectTypeDesc(cx, mod, defn, depth, (BLangObjectTypeNode) td); + case ERROR_TYPE -> resolveErrorTypeDesc(cx, mod, defn, depth, (BLangErrorType) td); + case TABLE_TYPE -> resolveTableTypeDesc(cx, mod, defn, depth, (BLangTableTypeNode) td); + case STREAM_TYPE -> resolveStreamTypeDesc(cx, mod, defn, depth, (BLangStreamType) td); + default -> throw new UnsupportedOperationException("type not implemented: " + td.getKind()); + }; + } + + private SemType resolveStreamTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangStreamType td) { + if (td.constraint == null) { + return Builder.getStreamType(); + } + Env env = (Env) cx.getInnerEnv(); + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + StreamDefinition sd = new StreamDefinition(); + attachedDefinitions.put(td, sd); + + SemType valueType = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + SemType completionType = td.error == null ? Builder.getNilType() : + resolveTypeDesc(cx, mod, defn, depth + 1, td.error); + return sd.define(env, valueType, completionType); + } + + private SemType resolveTableTypeDesc(TypeTestContext cx, + Map mod, BLangTypeDefinition defn, + int depth, BLangTableTypeNode td) { + SemType tableConstraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + Context context = (Context) cx.getInnerContext(); + if (td.tableKeySpecifier != null) { + List fieldNameIdentifierList = td.tableKeySpecifier.fieldNameIdentifierList; + String[] fieldNames = fieldNameIdentifierList.stream().map(IdentifierNode::getValue).toArray(String[]::new); + return TableUtils.tableContainingKeySpecifier(context, tableConstraint, fieldNames); + } + if (td.tableKeyTypeConstraint != null) { + SemType keyConstraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.tableKeyTypeConstraint.keyType); + return TableUtils.tableContainingKeyConstraint(context, tableConstraint, keyConstraint); + } + return TableUtils.tableContaining(context.env, tableConstraint); + } + + + private SemType resolveErrorTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangErrorType td) { + SemType innerType = createErrorType(cx, mod, defn, depth, td); + if (td.flagSet.contains(Flag.DISTINCT)) { + Env env = (Env) cx.getInnerEnv(); + return getDistinctErrorType(env, innerType); + } + return innerType; + } + + private static SemType getDistinctErrorType(Env env, SemType innerType) { + return Core.intersect(ErrorUtils.errorDistinct(env.distinctAtomCountGetAndIncrement()), innerType); + } + + private SemType createErrorType(TypeTestContext cx, Map mod, BLangTypeDefinition defn, + int depth, BLangErrorType td) { + if (td.detailType == null) { + return Builder.getErrorType(); + } else { + SemType detailType = resolveTypeDesc(cx, mod, defn, depth + 1, td.detailType); + return ErrorUtils.errorDetail(detailType); + } + } + + private SemType resolveObjectTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangObjectTypeNode td) { + SemType innerType = resolveNonDistinctObject(cx, mod, defn, depth, td); + if (td.flagSet.contains(Flag.DISTINCT)) { + return getDistinctObjectType((Env) cx.getInnerEnv(), innerType); + } + return innerType; + } + + private SemType resolveNonDistinctObject(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangObjectTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + ObjectDefinition od = new ObjectDefinition(); + attachedDefinitions.put(td, od); + Stream fieldStream = td.fields.stream().map(field -> { + Set flags = field.flagSet; + Member.Visibility visibility = flags.contains(Flag.PUBLIC) ? Member.Visibility.Public : + Member.Visibility.Private; + SemType ty = resolveTypeDesc(cx, mod, defn, depth + 1, field.typeNode); + return new Member(field.name.value, ty, Member.Kind.Field, visibility, flags.contains(Flag.READONLY)); + }); + Stream methodStream = td.getFunctions().stream().map(method -> { + Member.Visibility visibility = method.flagSet.contains(Flag.PUBLIC) ? Member.Visibility.Public : + Member.Visibility.Private; + SemType ty = resolveFunctionType(cx, mod, defn, depth + 1, method); + return new Member(method.name.value, ty, Member.Kind.Method, visibility, true); + }); + List members = Stream.concat(fieldStream, methodStream).toList(); + ObjectQualifiers qualifiers = getQualifiers(td); + return od.define(env, qualifiers, members, qualifiers.readonly() ? CELL_MUT_NONE : + CELL_MUT_LIMITED); + } + + private ObjectQualifiers getQualifiers(BLangObjectTypeNode td) { + Set flags = td.symbol.getFlags(); + ObjectQualifiers.NetworkQualifier networkQualifier; + assert !(flags.contains(Flag.CLIENT) && flags.contains(Flag.SERVICE)) : + "object can't be both client and service"; + if (flags.contains(Flag.CLIENT)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Client; + } else if (flags.contains(Flag.SERVICE)) { + networkQualifier = ObjectQualifiers.NetworkQualifier.Service; + } else { + networkQualifier = ObjectQualifiers.NetworkQualifier.None; + } + return new ObjectQualifiers(flags.contains(Flag.ISOLATED), flags.contains(Flag.READONLY), networkQualifier); + } + + private SemType resolveFunctionType(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, + int depth, BLangFunction functionType) { + Env env = (Env) cx.getInnerEnv(); + Definition attached = attachedDefinitions.get(functionType); + if (attached != null) { + return attached.getSemType(env); + } + FunctionDefinition fd = new FunctionDefinition(); + attachedDefinitions.put(functionType, fd); + Map paramScope = new HashMap<>(); + SemType[] params = getParameters(cx, mod, paramScope, defn, depth, functionType); + SemType rest; + if (functionType.getRestParameters() == null) { + rest = Builder.getNeverType(); + } else { + ArrayTypeNode arrayType = (ArrayTypeNode) functionType.getRestParameters().getTypeNode(); + rest = resolveTypeDesc(cx, mod, defn, depth + 1, arrayType.getElementType()); + } + SemType returnType = resolveReturnType(cx, mod, paramScope, defn, depth + 1, functionType.getReturnTypeNode()); + ListDefinition paramListDefinition = new ListDefinition(); + FunctionQualifiers qualifiers = FunctionQualifiers.create(functionType.flagSet.contains(Flag.ISOLATED), + functionType.flagSet.contains(Flag.TRANSACTIONAL)); + return fd.define(env, paramListDefinition.defineListTypeWrapped(env, params, params.length, rest, + CellAtomicType.CellMutability.CELL_MUT_NONE), returnType, qualifiers); + } + + private SemType[] getParameters(TypeTestContext cx, Map mod, + Map paramScope, BLangTypeDefinition defn, int depth, + BLangFunction functionType) { + List params = new ArrayList<>(); + if (functionType instanceof BLangResourceFunction resourceFunctionType) { + params.add(Builder.getStringConst(resourceFunctionType.methodName.value)); + for (var each : resourceFunctionType.resourcePathSegments) { + params.add(resolveTypeDesc(cx, mod, defn, depth + 1, each.typeNode)); + } + } + for (BLangSimpleVariable paramVar : functionType.getParameters()) { + SemType semType = resolveTypeDesc(cx, mod, defn, depth + 1, paramVar.typeNode); + if (Core.isSubtypeSimple(semType, Builder.getTypeDescType())) { + paramScope.put(paramVar.name.value, paramVar); + } + params.add(semType); + } + return params.toArray(SemType[]::new); + } + + private SemType getDistinctObjectType(Env env, SemType innerType) { + return Core.intersect(ObjectDefinition.distinct(env.distinctAtomCountGetAndIncrement()), innerType); + } + + private SemType resolveFunctionTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangFunctionTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + if (isFunctionTop(td)) { + if (td.flagSet.contains(Flag.ISOLATED) || td.flagSet.contains(Flag.TRANSACTIONAL)) { + FunctionDefinition fd = new FunctionDefinition(); + return fd.define(env, Builder.getNeverType(), Builder.getValType(), + FunctionQualifiers.create( + td.flagSet.contains(Flag.ISOLATED), + td.flagSet.contains(Flag.TRANSACTIONAL))); + } + return Builder.getFunctionType(); + } + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + FunctionDefinition fd = new FunctionDefinition(); + attachedDefinitions.put(td, fd); + + Map tdScope = new HashMap<>(); + List params = new ArrayList<>(td.params.size()); + for (BLangSimpleVariable param : td.params) { + SemType paramType = resolveTypeDesc(cx, mod, defn, depth + 1, param.typeNode); + params.add(paramType); + if (Core.isSubtypeSimple(paramType, Builder.getTypeDescType())) { + tdScope.put(param.name.value, param); + } + } + SemType rest; + if (td.restParam == null) { + rest = Builder.getNeverType(); + } else { + BLangArrayType restArrayType = (BLangArrayType) td.restParam.typeNode; + rest = resolveTypeDesc(cx, mod, defn, depth + 1, restArrayType.elemtype); + } + SemType returnType = resolveReturnType(cx, mod, tdScope, defn, depth + 1, td.returnTypeNode); + ListDefinition paramListDefinition = new ListDefinition(); + return fd.define(env, + paramListDefinition.defineListTypeWrapped(env, params.toArray(SemType[]::new), params.size(), rest, + CELL_MUT_NONE), + returnType, + FunctionQualifiers.create(td.flagSet.contains(Flag.ISOLATED), td.flagSet.contains(Flag.TRANSACTIONAL))); + } + + private SemType resolveReturnType(TypeTestContext cx, + Map mod, + Map mayBeDependentlyTypeNodes, + BLangTypeDefinition defn, + int depth, BLangType returnTypeNode) { + if (returnTypeNode == null) { + return Builder.getNilType(); + } + SemType innerType; + // Dependently typed function are quite rare so doing it via exception handling should be faster than actually + // checking if it is a dependently typed one. + boolean isDependentlyType; + try { + innerType = resolveTypeDesc(cx, mod, defn, depth + 1, returnTypeNode); + isDependentlyType = false; + } catch (IndexOutOfBoundsException err) { + innerType = + resolveDependentlyTypedReturnType(cx, mod, mayBeDependentlyTypeNodes, defn, depth, returnTypeNode); + isDependentlyType = true; + } + ListDefinition ld = new ListDefinition(); + return ld.defineListTypeWrapped((Env) cx.getInnerEnv(), + new SemType[]{!isDependentlyType ? Builder.getBooleanType() : Builder.getBooleanConst(true), + innerType}, 2, Builder.getNeverType(), + CELL_MUT_LIMITED); + } + + private SemType resolveDependentlyTypedReturnType(TypeTestContext cx, + Map mod, + Map mayBeDependentlyTypeNodes, + BLangTypeDefinition defn, int depth, + TypeNode returnTypeNode) { + Map combined = new HashMap<>(mod); + combined.putAll(mayBeDependentlyTypeNodes); + return resolveTypeDesc(cx, combined, defn, depth + 1, returnTypeNode); + } + + private boolean isFunctionTop(BLangFunctionTypeNode td) { + return td.params.isEmpty() && td.restParam == null && td.returnTypeNode == null; + } + + private SemType resolveRecordTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangRecordTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + + MappingDefinition md = new MappingDefinition(); + attachedDefinitions.put(td, md); + + MappingDefinition.Field[] fields = new MappingDefinition.Field[td.fields.size()]; + for (int i = 0; i < td.fields.size(); i++) { + BLangSimpleVariable field = td.fields.get(i); + SemType type = resolveTypeDesc(cx, mod, defn, depth + 1, field.typeNode); + if (Core.isNever(type)) { + throw new IllegalStateException("record field can't be never"); + } + fields[i] = + new MappingDefinition.Field(field.name.value, type, field.flagSet.contains(Flag.READONLY), + field.flagSet.contains(Flag.OPTIONAL)); + } + + SemType rest; + if (!td.isSealed() && td.getRestFieldType() == null) { + rest = Builder.getAnyDataType(); + } else { + rest = resolveTypeDesc(cx, mod, defn, depth + 1, td.getRestFieldType()); + } + if (rest == null) { + rest = Builder.getNeverType(); + } + return md.defineMappingTypeWrapped((Env) cx.getInnerEnv(), fields, rest, CELL_MUT_LIMITED); + } + + private SemType resolveConstrainedTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangConstrainedType td) { + BLangBuiltInRefTypeNode refTypeNode = (BLangBuiltInRefTypeNode) td.getType(); + return switch (refTypeNode.typeKind) { + case MAP -> resolveMapTypeDesc(cx, mod, defn, depth, td); + case FUTURE -> resolveFutureTypeDesc(cx, mod, defn, depth, td); + case XML -> resolveXmlTypeDesc(cx, mod, defn, depth, td); + case TYPEDESC -> resolveTypedescTypeDesc(cx, mod, defn, depth, td); + default -> throw new UnsupportedOperationException( + "Constrained type not implemented: " + refTypeNode.typeKind); + }; + } + + private SemType resolveTypedescTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangConstrainedType td) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return TypedescUtils.typedescContaining((Env) cx.getInnerEnv(), constraint); + } + + private SemType resolveFutureTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangConstrainedType td) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return FutureUtils.futureContaining((Env) cx.getInnerEnv(), constraint); + } + + private SemType resolveXmlTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangConstrainedType td) { + SemType constraint = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + return XmlUtils.xmlSequence(constraint); + } + + private SemType resolveMapTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangConstrainedType td) { + Env env = (Env) cx.getInnerEnv(); + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + + MappingDefinition md = new MappingDefinition(); + attachedDefinitions.put(td, md); + + SemType rest = resolveTypeDesc(cx, mod, defn, depth + 1, td.constraint); + + return md.defineMappingTypeWrapped(env, new MappingDefinition.Field[0], rest, CELL_MUT_LIMITED); + } + + private SemType resolveTupleTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangTupleTypeNode td) { + Env env = (Env) cx.getInnerEnv(); + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType(env); + } + ListDefinition ld = new ListDefinition(); + attachedDefinitions.put(td, ld); + SemType[] memberSemTypes = td.members.stream() + .map(member -> resolveTypeDesc(cx, mod, defn, depth + 1, member.typeNode)) + .toArray(SemType[]::new); + SemType rest = + td.restParamType != null ? resolveTypeDesc(cx, mod, defn, depth + 1, td.restParamType) : + Builder.getNeverType(); + return ld.defineListTypeWrapped(env, memberSemTypes, memberSemTypes.length, rest, CELL_MUT_LIMITED); + } + + private SemType resolveArrayTypeDesc(TypeTestContext cx, Map mod, + BLangTypeDefinition defn, int depth, BLangArrayType td) { + Definition attachedDefinition = attachedDefinitions.get(td); + if (attachedDefinition != null) { + return attachedDefinition.getSemType((Env) cx.getInnerEnv()); + } + + ListDefinition ld = new ListDefinition(); + attachedDefinitions.put(td, ld); + + int dimensions = td.dimensions; + SemType accum = resolveTypeDesc(cx, mod, defn, depth + 1, td.elemtype); + for (int i = 0; i < dimensions; i++) { + int size = from(mod, td.sizes.get(i)); + if (i == dimensions - 1) { + accum = resolveListInner(cx, ld, size, accum); + } else { + accum = resolveListInner(cx, size, accum); + } + } + return accum; + } + + private SemType resolveListInner(TypeTestContext cx, int size, SemType eType) { + ListDefinition ld = new ListDefinition(); + return resolveListInner(cx, ld, size, eType); + } + + private static SemType resolveListInner(TypeTestContext cx, ListDefinition ld, int size, SemType eType) { + Env env = (Env) cx.getInnerEnv(); + if (size != -1) { + SemType[] members = {eType}; + return ld.defineListTypeWrapped(env, members, Math.abs(size), Builder.getNeverType(), CELL_MUT_LIMITED); + } else { + return ld.defineListTypeWrapped(env, EMPTY_SEMTYPE_ARR, 0, eType, CELL_MUT_LIMITED); + } + } + + + private SemType resolveSingletonType(BLangFiniteTypeNode td) { + return td.valueSpace.stream().map(each -> (BLangLiteral) each) + .map(literal -> resolveSingletonType(literal.value, literal.getDeterminedType().getKind()).get()) + .reduce(Builder.getNeverType(), Core::union); + } + + private Optional resolveSingletonType(Object value, TypeKind targetTypeKind) { + return switch (targetTypeKind) { + case NIL -> Optional.of(Builder.getNilType()); + case BOOLEAN -> Optional.of(Builder.getBooleanConst((Boolean) value)); + case INT, BYTE -> { + assert !(value instanceof Byte); + yield Optional.of(Builder.getIntConst(((Number) value).longValue())); + } + case FLOAT -> { + double doubleVal; + if (value instanceof Long longValue) { + doubleVal = longValue.doubleValue(); + } else if (value instanceof Double doubleValue) { + doubleVal = doubleValue; + } else { + // literal value will be a string if it wasn't within the bounds of what is supported by Java Long + // or Double when it was parsed in BLangNodeBuilder. + try { + doubleVal = Double.parseDouble((String) value); + } catch (NumberFormatException e) { + yield Optional.empty(); + } + } + yield Optional.of(Builder.getFloatConst(doubleVal)); + } + case DECIMAL -> { + String repr = (String) value; + if (repr.contains("d") || repr.contains("D")) { + repr = repr.substring(0, repr.length() - 1); + } + yield Optional.of(Builder.getDecimalConst(new BigDecimal(repr))); + } + case STRING -> Optional.of(Builder.getStringConst((String) value)); + case HANDLE -> Optional.of(Builder.getHandleType()); + default -> Optional.empty(); + }; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangUnionTypeNode td, Map mod, + int depth, BLangTypeDefinition defn) { + + Iterator iterator = td.memberTypeNodes.iterator(); + SemType res = resolveTypeDesc(cx, mod, defn, depth, iterator.next()); + while (iterator.hasNext()) { + res = Core.union(res, resolveTypeDesc(cx, mod, defn, depth, iterator.next())); + } + return res; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangUserDefinedType td, Map mod, + int depth) { + String name = td.typeName.value; + // Need to replace this with a real package lookup + if (td.pkgAlias.value.equals("int")) { + return resolveIntSubtype(name); + } else if (td.pkgAlias.value.equals("string") && name.equals("Char")) { + return Builder.getCharType(); + } else if (td.pkgAlias.value.equals("xml")) { + return resolveXmlSubType(name); + } else if (td.pkgAlias.value.equals("regexp") && name.equals("RegExp")) { + return Builder.getRegexType(); + } + + BLangNode moduleLevelDef = mod.get(name); + if (moduleLevelDef == null) { + throw new IndexOutOfBoundsException("unknown type " + name); + } + + switch (moduleLevelDef.getKind()) { + case TYPE_DEFINITION -> { + SemType ty = resolveTypeDefnRec(cx, mod, (BLangTypeDefinition) moduleLevelDef, depth); + if (td.flagSet.contains(Flag.DISTINCT)) { + return getDistinctSemType(cx, ty); + } + return ty; + } + case CONSTANT -> { + BLangConstant constant = (BLangConstant) moduleLevelDef; + return resolveTypeDefnRec(cx, mod, constant.getAssociatedTypeDefinition(), depth); + } + case VARIABLE -> { + // This happens when the type is a parameter of a dependently typed function + BLangVariable variable = (BLangVariable) moduleLevelDef; + BLangConstrainedType typeDescType = (BLangConstrainedType) variable.getTypeNode(); + return resolveTypeDesc(cx, mod, null, depth, typeDescType.constraint); + } + default -> throw new UnsupportedOperationException("class defns not implemented"); + } + } + + private SemType resolveXmlSubType(String name) { + return switch (name) { + case "Element" -> Builder.getXmlElementType(); + case "Comment" -> Builder.getXmlCommentType(); + case "Text" -> Builder.getXmlTextType(); + case "ProcessingInstruction" -> Builder.getXmlPIType(); + default -> throw new IllegalStateException("Unknown XML subtype: " + name); + }; + } + + private SemType getDistinctSemType(TypeTestContext cx, SemType innerType) { + Env env = (Env) cx.getInnerEnv(); + if (Core.isSubtypeSimple(innerType, Builder.getObjectType())) { + return getDistinctObjectType(env, innerType); + } else if (Core.isSubtypeSimple(innerType, Builder.getErrorType())) { + return getDistinctErrorType(env, innerType); + } + throw new IllegalArgumentException("Distinct type not supported for: " + innerType); + } + + private SemType resolveIntSubtype(String name) { + // TODO: support MAX_VALUE + return switch (name) { + case "Signed8" -> Builder.createIntRange(SIGNED8_MIN_VALUE, SIGNED8_MAX_VALUE); + case "Signed16" -> Builder.createIntRange(SIGNED16_MIN_VALUE, SIGNED16_MAX_VALUE); + case "Signed32" -> Builder.createIntRange(SIGNED32_MIN_VALUE, SIGNED32_MAX_VALUE); + case "Unsigned8" -> Builder.createIntRange(0, UNSIGNED8_MAX_VALUE); + case "Unsigned16" -> Builder.createIntRange(0, UNSIGNED16_MAX_VALUE); + case "Unsigned32" -> Builder.createIntRange(0, UNSIGNED32_MAX_VALUE); + default -> throw new UnsupportedOperationException("Unknown int subtype: " + name); + }; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangIntersectionTypeNode td, + Map mod, int depth, BLangTypeDefinition defn) { + Iterator iterator = td.constituentTypeNodes.iterator(); + SemType res = resolveTypeDesc(cx, mod, defn, depth, iterator.next()); + while (iterator.hasNext()) { + res = Core.intersect(res, resolveTypeDesc(cx, mod, defn, depth, iterator.next())); + } + return res; + } + + private SemType resolveTypeDesc(BLangBuiltInRefTypeNode td) { + return switch (td.typeKind) { + case NEVER -> Builder.getNeverType(); + case XML -> Builder.getXmlType(); + case FUTURE -> Builder.getFutureType(); + // TODO: implement json type + + default -> throw new UnsupportedOperationException("Built-in ref type not implemented: " + td.typeKind); + }; + } + + private SemType resolveTypeDesc(TypeTestContext cx, BLangValueType td) { + return switch (td.typeKind) { + case NIL -> Builder.getNilType(); + case BOOLEAN -> Builder.getBooleanType(); + case BYTE -> Builder.createIntRange(0, UNSIGNED8_MAX_VALUE); + case INT -> Builder.getIntType(); + case FLOAT -> Builder.getFloatType(); + case DECIMAL -> Builder.getDecimalType(); + case STRING -> Builder.getStringType(); + case READONLY -> Builder.getReadonlyType(); + case ANY -> Builder.getAnyType(); + case ANYDATA -> Builder.getAnyDataType(); + case ERROR -> Builder.getErrorType(); + case XML -> Builder.getXmlType(); + case HANDLE -> Builder.getHandleType(); + case TYPEDESC -> Builder.getTypeDescType(); + default -> throw new IllegalStateException("Unknown type: " + td); + }; + } + + private SemType evaluateConst(BLangConstant constant) { + return switch (constant.symbol.value.type.getKind()) { + case INT -> Builder.getIntConst((long) constant.symbol.value.value); + case BOOLEAN -> Builder.getBooleanConst((boolean) constant.symbol.value.value); + case STRING -> Builder.getStringConst((String) constant.symbol.value.value); + case FLOAT -> Builder.getFloatConst((double) constant.symbol.value.value); + default -> throw new UnsupportedOperationException("Expression type not implemented for const semtype"); + }; + } + + private void attachToBType(BLangType bType, SemType semType) { + attachedSemType.put(bType, semType); + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestAPI.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestAPI.java new file mode 100644 index 000000000000..cd2d6e0671ae --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestAPI.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +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.ListProj; +import io.ballerina.runtime.api.types.semtype.MappingProj; +import io.ballerina.runtime.api.types.semtype.SemType; + +public class RuntimeTypeTestAPI implements TypeTestAPI { + + private static final RuntimeTypeTestAPI INSTANCE = new RuntimeTypeTestAPI(); + + private RuntimeTypeTestAPI() { + } + + public static RuntimeTypeTestAPI getInstance() { + return INSTANCE; + } + + @Override + public boolean isSubtype(TypeTestContext cx, SemType t1, SemType t2) { + return Core.isSubType(from(cx), t1, t2); + } + + private static Context from(TypeTestContext cx) { + return (Context) cx.getInnerContext(); + } + + @Override + public boolean isSubtypeSimple(SemType t1, SemType t2) { + return Core.isSubtypeSimple(t1, t2); + } + + @Override + public boolean isListType(SemType t) { + return Core.isSubtypeSimple(t, Builder.getListType()); + } + + @Override + public boolean isMapType(SemType t) { + return Core.isSubtypeSimple(t, Builder.getMappingType()); + } + + @Override + public SemType intConst(long l) { + return Builder.getIntConst(l); + } + + @Override + public SemType mappingMemberTypeInnerVal(TypeTestContext context, SemType t, SemType key) { + return MappingProj.mappingMemberTypeInnerVal(from(context), t, key); + } + + @Override + public SemType listProj(TypeTestContext context, SemType t, SemType key) { + return ListProj.listProjInnerVal(from(context), t, key); + } + + @Override + public SemType listMemberType(TypeTestContext context, SemType t, SemType key) { + return Core.listMemberTypeInnerVal(from(context), t, key); + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestContext.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestContext.java new file mode 100644 index 000000000000..add06c8db8c5 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestContext.java @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.runtime.api.types.semtype.Context; +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +public final class RuntimeTypeTestContext implements TypeTestContext { + + private final TypeTestEnv env; + private final Context cx; + + private RuntimeTypeTestContext(TypeTestEnv env) { + this.env = env; + this.cx = Context.from((Env) env.getInnerEnv()); + } + + public static synchronized RuntimeTypeTestContext from(TypeTestEnv env) { + return new RuntimeTypeTestContext(env); + } + + @Override + public TypeTestEnv getEnv() { + return env; + } + + @Override + public Object getInnerEnv() { + return env.getInnerEnv(); + } + + @Override + public Object getInnerContext() { + return cx; + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestEnv.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestEnv.java new file mode 100644 index 000000000000..2592d6ba5565 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/RuntimeTypeTestEnv.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.types.semtype.SemType; + +import java.util.HashMap; +import java.util.Map; + +class RuntimeTypeTestEnv implements TypeTestEnv { + + private final Map typeMap = new HashMap<>(); + private final Env env; + + private RuntimeTypeTestEnv(Env env) { + this.env = env; + } + + public static synchronized RuntimeTypeTestEnv from(Env env) { + return new RuntimeTypeTestEnv(env); + } + + @Override + public Map getTypeNameSemTypeMap() { + return typeMap; + } + + @Override + public void addTypeDef(String value, SemType semtype) { + typeMap.put(value, semtype); + } + + @Override + public Object getInnerEnv() { + return env; + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeAssertionTransformer.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeAssertionTransformer.java new file mode 100644 index 000000000000..b43a4fa84f33 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeAssertionTransformer.java @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2022, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.semtype.port.test; + +import io.ballerina.compiler.syntax.tree.Minutiae; +import io.ballerina.compiler.syntax.tree.MinutiaeList; +import io.ballerina.compiler.syntax.tree.ModuleMemberDeclarationNode; +import io.ballerina.compiler.syntax.tree.ModulePartNode; +import io.ballerina.compiler.syntax.tree.Node; +import io.ballerina.compiler.syntax.tree.NodeVisitor; +import io.ballerina.compiler.syntax.tree.SyntaxKind; +import io.ballerina.compiler.syntax.tree.SyntaxTree; +import io.ballerina.compiler.syntax.tree.Token; +import org.testng.Assert; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +/** + * Extract semtype assertions in the below form. + * @param which semtype (runtime or compiler) used in the assertions. + * // @type A < B + * // @type B = C + * // @type c <> D + * @param Actual semtype definition on which assertion is defined on (runtime or compile time) + * @since 3.0.0 + */ +public final class SemTypeAssertionTransformer extends NodeVisitor { + + private final String fileName; + private final SyntaxTree syntaxTree; + private final TypeTestEnv semtypeEnv; + private final TypeTestContext context; + private final List list; + private final TypeTestAPI semtypeAPI; + + private SemTypeAssertionTransformer(String fileName, SyntaxTree syntaxTree, TypeTestEnv semtypeEnv, + TypeTestContext context, TypeTestAPI semtypeAPI) { + this.fileName = fileName; + this.syntaxTree = syntaxTree; + this.semtypeEnv = semtypeEnv; + this.context = context; + list = new ArrayList<>(); + this.semtypeAPI = semtypeAPI; + } + + public static List> getTypeAssertionsFrom(String fileName, + SyntaxTree syntaxTree, + TypeTestEnv semtypeEnv, + TypeTestContext context, + TypeTestAPI api) { + final SemTypeAssertionTransformer t = + new SemTypeAssertionTransformer<>(fileName, syntaxTree, semtypeEnv, context, api); + return t.getTypeAssertions(); + } + + private List> getTypeAssertions() { + syntaxTree.rootNode().accept(this); + List> assertions = new ArrayList<>(); + for (String str : list) { + String[] parts = splitAssertion(str); + if (parts == null) { + continue; + } + SemType lhs = toSemType(parts[0]); + RelKind kind = RelKind.fromString(parts[1], str); + SemType rhs = toSemType(parts[2]); + String text = parts[0] + " " + parts[1] + " " + parts[2]; + assertions.add(new TypeAssertion<>(this.context, this.fileName, lhs, rhs, kind, text)); + } + return assertions; + } + + private SemType toSemType(String typeExpr) { + // Simple type reference + int leftBracketPos = typeExpr.indexOf('['); + final Map typeNameSemTypeMap = semtypeEnv.getTypeNameSemTypeMap(); + if (leftBracketPos == -1) { + SemType referredType = typeNameSemTypeMap.get(typeExpr); + if (referredType == null) { + throw new IllegalArgumentException("No such type: " + typeExpr); + } + return referredType; + } + int rightBracketPos = typeExpr.indexOf(']'); + String typeRef = typeExpr.substring(0, leftBracketPos); + String memberAccessExpr = typeExpr.substring(leftBracketPos + 1, rightBracketPos); + + SemType type = typeNameSemTypeMap.get(typeRef); + if (semtypeAPI.isListType(type)) { + SemType m; + try { + long l = Long.parseLong(memberAccessExpr); + m = semtypeAPI.intConst(l); + } catch (Exception e) { + // parsing number failed, access must be a type-reference + m = typeNameSemTypeMap.get(memberAccessExpr); + } + return listProj(context, type, m); + } else if (semtypeAPI.isMapType(type)) { + SemType m = typeNameSemTypeMap.get(memberAccessExpr); + return semtypeAPI.mappingMemberTypeInnerVal(context, type, m); + } + throw new IllegalStateException("Unsupported type test: " + typeExpr); + } + + private SemType listProj(TypeTestContext context, SemType t, SemType m) { + SemType s1 = semtypeAPI.listProj(context, t, m); + SemType s2 = semtypeAPI.listMemberType(context, t, m); + if (!semtypeAPI.isSubtype(context, s1, s2)) { + Assert.fail("listProj result is not a subtype of listMemberType"); + } + return s1; + } + + private String[] splitAssertion(String str) { + if (ignoredCommentSet().contains(str)) { + return null; + } + String[] parts = str.split(" "); + + if (parts[1].equals("-@type")) { + // TODO: remove this check once diff operator is supported + return null; + } + + // Only accept the form: `//` `@type` T1 REL T2 + if (!parts[1].equals("@type") || parts.length != 5) { + throw new IllegalStateException("Invalid type assertion '" + str + + "', expected in form: '// @type T1 REL T2'"); + } + return Arrays.copyOfRange(parts, 2, 5); + } + + /** + * Returns a set of comments that are to be ignored during the processing. + * These comments include non-essential information or comments that do not conform to the expected format. + * + * @return Set of comments to be ignored. + */ + public final HashSet ignoredCommentSet() { + HashSet hashSet = new HashSet<>(); + hashSet.add("// the order of type defns are intentional"); + return hashSet; + } + + @Override + protected void visitSyntaxNode(Node node) { + addComments(node.leadingMinutiae()); + addComments(node.trailingMinutiae()); + } + + @Override + public void visit(Token token) { + addComments(token.leadingMinutiae()); + addComments(token.trailingMinutiae()); + } + + private void addComments(MinutiaeList minutiaes) { + for (Minutiae minutiae : minutiaes) { + if (minutiae.kind() == SyntaxKind.COMMENT_MINUTIAE) { + String text = minutiae.text(); + if (text.length() > 2) { + list.add(text); + } + } + } + } + + @Override + public void visit(ModulePartNode modulePartNode) { + for (ModuleMemberDeclarationNode member : modulePartNode.members()) { + member.accept(this); + } + modulePartNode.eofToken().accept(this); + } + + /** + * Relationship to be asserted. + * + * @since 3.0.0 + */ + public enum RelKind { + SUB, SAME, NON; + + static RelKind fromString(String rel, String assertion) { + switch (rel) { + case "<": + return SUB; + case "=": + return SAME; + case "<>": + return NON; + default: + throw new AssertionError("Unknown type relationship in assertion: " + rel + "in: " + assertion); + } + } + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeResolver.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeResolver.java new file mode 100644 index 000000000000..288983862e21 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeResolver.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import org.ballerinalang.model.tree.NodeKind; +import org.wso2.ballerinalang.compiler.tree.BLangNode; +import org.wso2.ballerinalang.compiler.tree.BLangTypeDefinition; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangConstant; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; +import org.wso2.ballerinalang.compiler.tree.expressions.BLangSimpleVarRef; + +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static org.wso2.ballerinalang.compiler.semantics.analyzer.SymbolEnter.getTypeOrClassName; + +/** + * Abstract implementation of a type resolver that can be used for type tests. + * + * @param SemType implementation used + * @since 2201.11.0 + */ +public abstract class SemTypeResolver { + + protected static int from(Map mod, BLangNode expr) { + if (expr instanceof BLangLiteral literal) { + return SemTypeResolver.listSize((Number) literal.value); + } else if (expr instanceof BLangSimpleVarRef varRef) { + String varName = varRef.variableName.value; + return SemTypeResolver.from(mod, mod.get(varName)); + } else if (expr instanceof BLangConstant constant) { + return SemTypeResolver.listSize((Number) constant.symbol.value.value); + } + throw new UnsupportedOperationException("Unsupported expr kind " + expr.getKind()); + } + + private static int listSize(Number size) { + if (size.longValue() > Integer.MAX_VALUE) { + throw new IllegalArgumentException("list sizes greater than " + Integer.MAX_VALUE + " not yet supported"); + } + return size.intValue(); + } + + public void defineSemTypes(List moduleDefs, TypeTestContext cx) { + Map modTable = new LinkedHashMap<>(); + for (BLangNode typeAndClassDef : moduleDefs) { + modTable.put(getTypeOrClassName(typeAndClassDef), typeAndClassDef); + } + modTable = Collections.unmodifiableMap(modTable); + + for (BLangNode def : moduleDefs) { + if (def.getKind() == NodeKind.CLASS_DEFN) { + throw new UnsupportedOperationException("Semtype are not supported for class definitions yet"); + } else if (def.getKind() == NodeKind.CONSTANT) { + resolveConstant(cx, modTable, (BLangConstant) def); + } else { + BLangTypeDefinition typeDefinition = (BLangTypeDefinition) def; + resolveTypeDefn(cx, modTable, typeDefinition); + } + } + } + + protected abstract void resolveConstant(TypeTestContext cx, + Map modTable, BLangConstant constant); + + protected abstract void resolveTypeDefn(TypeTestContext cx, + Map mod, BLangTypeDefinition defn); +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeTest.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeTest.java new file mode 100644 index 000000000000..9ee71aaffbc9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/SemTypeTest.java @@ -0,0 +1,441 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package io.ballerina.semtype.port.test; + +import io.ballerina.runtime.api.types.semtype.Env; +import io.ballerina.runtime.api.utils.StringUtils; +import io.ballerina.runtime.internal.utils.ValueComparisonUtils; +import io.ballerina.tools.diagnostics.Diagnostic; +import io.ballerina.tools.diagnostics.DiagnosticSeverity; +import io.ballerina.types.Context; +import io.ballerina.types.SemType; +import io.ballerina.types.SemTypes; +import org.ballerinalang.test.BCompileUtil; +import org.jetbrains.annotations.NotNull; +import org.testng.Assert; +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import org.wso2.ballerinalang.compiler.semantics.model.Scope; +import org.wso2.ballerinalang.compiler.tree.BLangNode; +import org.wso2.ballerinalang.compiler.tree.BLangPackage; +import org.wso2.ballerinalang.compiler.util.Name; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.StringJoiner; +import java.util.function.Predicate; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * Test semtypes using compiler front-end for parsing. + * + * @since 2201.10.0 + */ +public class SemTypeTest { + + @DataProvider(name = "dataDirFileNameProvider") + public Object[] dataDirFileNameProvider() { + File dataDir = resolvePath("test-src/data").toFile(); + List testFiles = Arrays.stream(dataDir.listFiles()) + .map(File::getAbsolutePath) + .filter(name -> name.endsWith(".bal") && + !dataDirSkipList().contains(name.substring(name.lastIndexOf(File.separator) + 1))) + .collect(Collectors.toList()); + + include(testFiles, + "test-src/simple-type/float-altered.bal", + "test-src/simple-type/function-altered.bal", + "test-src/simple-type/int-singleton-altered.bal", + "test-src/simple-type/list-type-test.bal", + "test-src/simple-type/map-type-test.bal", + "test-src/simple-type/type-test.bal" + ); + + return testFiles.toArray(String[]::new); + //return new Object[]{"test-src/data/error2.bal"}; + } + + public final HashSet dataDirSkipList() { + HashSet hashSet = new HashSet<>(); + return hashSet; + } + + @DataProvider(name = "fileNameProviderFunc") + public Object[] fileNameProviderFunc() { + File dataDir = resolvePath("test-src/localVar").toFile(); + List testFiles = Arrays.stream(dataDir.listFiles()) + .map(File::getAbsolutePath) + .filter(name -> name.endsWith(".bal")) + .toList(); + + return testFiles.toArray(new String[0]); + } + + @DataProvider(name = "type-rel-provider") + public Object[] typeRelTestFileNameProvider() { + File dataDir = resolvePath("test-src/type-rel").toFile(); + List balFiles = new ArrayList<>(); + listAllBalFiles(dataDir, balFiles); + Collections.sort(balFiles); + + Collection> tests = new ArrayList<>(); + for (File file : balFiles) { + TypeCheckData utils = compilerTypeResolverUtilsFromFile(file); + List> assertions = + getTypeAssertions(file, utils.resolver(), utils.context(), utils.env(), + CompilerTypeTestAPI.getInstance(), utils.pair()); + tests.addAll(assertions); + } + return tests.toArray(); + } + + @NotNull + private static List> getTypeAssertions(File file, + SemTypeResolver typeResolver, + TypeTestContext typeCheckContext, + TypeTestEnv typeTestEnv, + TypeTestAPI api, + BCompileUtil.PackageSyntaxTreePair pair) { + String fileName = file.getAbsolutePath(); + BLangPackage pkgNode = pair.bLangPackage; + + List typeAndClassDefs = new ArrayList<>(); + typeAndClassDefs.addAll(pkgNode.constants); + typeAndClassDefs.addAll(pkgNode.typeDefinitions); + + try { + typeResolver.defineSemTypes(typeAndClassDefs, typeCheckContext); + return SemTypeAssertionTransformer.getTypeAssertionsFrom(fileName, pair.syntaxTree, typeTestEnv, + typeCheckContext, api); + } catch (Exception e) { + return List.of(new TypeAssertion<>( + null, fileName, null, null, null, e.getMessage() + )); + } + } + + public void listAllBalFiles(File file, List balFiles) { + if (file.isFile()) { + return; + } + for (File f : file.listFiles()) { + if (f.isDirectory()) { + listAllBalFiles(f, balFiles); + } + String fileName = f.getName(); + if (fileName.endsWith(".bal") && !typeRelDirSkipList().contains(fileName)) { + balFiles.add(f); + } + } + } + + public final HashSet typeRelDirSkipList() { + HashSet hashSet = new HashSet<>(); + // intersection with negative (!) atom + hashSet.add("proj7-tv.bal"); + hashSet.add("proj8-tv.bal"); + hashSet.add("proj9-tv.bal"); + hashSet.add("proj10-tv.bal"); + + // Not a type test. This is an error test + hashSet.add("xml-te.bal"); + return hashSet; + } + + private void include(List testFiles, String... fileNames) { + for (int i = 0; i < fileNames.length; i++) { + testFiles.add(i, fileNames[i]); + } + } + + @Test(dataProvider = "dataDirFileNameProvider") + public void verifyAllSubtypeRelationships(String fileName) { + List subtypeRels = getSubtypeRels(fileName); + List expectedRels = extractSubtypeRelations(fileName); + // Commented code will get expected content for this test to pass. + // Useful for taking a diff. + // String text = toText(subtypeRels); + Assert.assertEquals(subtypeRels, expectedRels); + } + + @Test(dataProvider = "fileNameProviderFunc") + public void funcTest(String fileName) { + BCompileUtil.PackageSyntaxTreePair packageSyntaxTreePair = BCompileUtil.compileSemType(fileName); + BLangPackage bLangPackage = packageSyntaxTreePair.bLangPackage; + ensureNoErrors(bLangPackage); + List vars = extractVarTypes(fileName); + Context tc = Context.from(bLangPackage.semtypeEnv); + Scope globalScope = bLangPackage.symbol.scope; + bLangPackage.functions.forEach(func -> { + Scope scope = func.getBody().scope; + vars.forEach(v -> { + if (v.length != 2) { + Assert.fail("test structure should be `variable = Type`"); + } + SemType t1 = scope.lookup(new Name(v[0])).symbol.type.semType(); + SemType t2 = globalScope.lookup(new Name(v[1])).symbol.type.semType(); + + String msg = "semtype in local scope is different from global scope"; + Assert.assertTrue(SemTypes.isSubtype(tc, t1, t2), msg); + Assert.assertTrue(SemTypes.isSubtype(tc, t2, t1), msg); + }); + }); + } + + @Test(expectedExceptions = AssertionError.class) + public void shouldFailForIncorrectTestStructure() { + File wrongAssertionFile = resolvePath("test-src/type-rel-wrong.bal").toFile(); + TypeCheckData utils = compilerTypeResolverUtilsFromFile(wrongAssertionFile); + List> typeAssertions = getTypeAssertions(wrongAssertionFile, + utils.resolver(), utils.context(), utils.env(), CompilerTypeTestAPI.getInstance(), utils.pair() + ); + testSemTypeAssertions(typeAssertions.get(0)); + } + + @DataProvider(name = "runtimeFileNameProviderFunc") + public Object[] runtimeFileNameProviderFunc() { + File dataDir = resolvePath("test-src/type-rel").toFile(); + List balFiles = new ArrayList<>(); + listAllBalFiles(dataDir, balFiles); + Collections.sort(balFiles); + return balFiles.stream() + .map(File::getAbsolutePath).toArray(); + } + + private static Predicate createRuntimeFileNameFilter(Set skipList) { + return file -> file.getName().endsWith(".bal") && !skipList.contains(file.getName()); + } + + @Test(dataProvider = "runtimeFileNameProviderFunc") + public void testRuntimeSemTypes(String fileName) { + File file = resolvePath(fileName).toFile(); + var utils = runtimeTypeResolverUtilsFromFile(file); + RuntimeTypeTestAPI api = RuntimeTypeTestAPI.getInstance(); + getTypeAssertions(file, + utils.resolver(), utils.context(), utils.env(), api, utils.pair()) + .forEach(a -> testAssertion(a, api)); + } + + private static TypeCheckData compilerTypeResolverUtilsFromFile(File file) { + String fileName = file.getAbsolutePath(); + BCompileUtil.PackageSyntaxTreePair pair = BCompileUtil.compileSemType(fileName); + BLangPackage pkgNode = pair.bLangPackage; + TypeTestContext context = ComplierTypeTestContext.from(Context.from(pkgNode.semtypeEnv)); + TypeTestEnv env = CompilerTypeTestEnv.from(pkgNode.semtypeEnv); + SemTypeResolver resolver = new CompilerSemTypeResolver(); + return new TypeCheckData<>(pair, context, env, resolver); + } + + private static TypeCheckData runtimeTypeResolverUtilsFromFile( + File file) { + String fileName = file.getAbsolutePath(); + BCompileUtil.PackageSyntaxTreePair pair = BCompileUtil.compileSemType(fileName); + TypeTestEnv env = RuntimeTypeTestEnv.from(Env.getInstance()); + TypeTestContext context = RuntimeTypeTestContext.from(env); + SemTypeResolver resolver = new RuntimeSemTypeResolver(); + return new TypeCheckData<>(pair, context, env, resolver); + } + + private record TypeCheckData(BCompileUtil.PackageSyntaxTreePair pair, TypeTestContext context, + TypeTestEnv env, SemTypeResolver resolver) { + + } + + @Test(expectedExceptions = AssertionError.class) + public void shouldFailForTooLargeLists() { + File wrongAssertionFile = resolvePath("test-src/fixed-length-array-too-large-te.bal").toFile(); + TypeCheckData utils = compilerTypeResolverUtilsFromFile(wrongAssertionFile); + List> typeAssertions = getTypeAssertions(wrongAssertionFile, + utils.resolver(), utils.context(), utils.env(), CompilerTypeTestAPI.getInstance(), utils.pair() + ); + testSemTypeAssertions(typeAssertions.get(0)); + } + + @Test(dataProvider = "type-rel-provider") + public void testSemTypeAssertions(TypeAssertion typeAssertion) { + if (typeAssertion.kind() == null) { + Assert.fail( + "Exception thrown in " + typeAssertion.fileName() + System.lineSeparator() + typeAssertion.text()); + } + testAssertion(typeAssertion, CompilerTypeTestAPI.getInstance()); + } + + private void testAssertion(TypeAssertion typeAssertion, + TypeTestAPI semTypes) { + switch (typeAssertion.kind()) { + case NON: + Assert.assertFalse( + semTypes.isSubtype(typeAssertion.context(), typeAssertion.lhs(), typeAssertion.rhs()), + formatFailingAssertionDescription(typeAssertion)); + Assert.assertFalse( + semTypes.isSubtype(typeAssertion.context(), typeAssertion.rhs(), typeAssertion.lhs()), + formatFailingAssertionDescription(typeAssertion)); + break; + case SUB: + Assert.assertTrue(semTypes.isSubtype(typeAssertion.context(), typeAssertion.lhs(), typeAssertion.rhs()), + formatFailingAssertionDescription(typeAssertion)); + Assert.assertFalse( + semTypes.isSubtype(typeAssertion.context(), typeAssertion.rhs(), typeAssertion.lhs()), + formatFailingAssertionDescription(typeAssertion)); + break; + case SAME: + Assert.assertTrue(semTypes.isSubtype(typeAssertion.context(), typeAssertion.lhs(), typeAssertion.rhs()), + formatFailingAssertionDescription(typeAssertion)); + Assert.assertTrue(semTypes.isSubtype(typeAssertion.context(), typeAssertion.rhs(), typeAssertion.lhs()), + formatFailingAssertionDescription(typeAssertion)); + } + } + + @NotNull + private String formatFailingAssertionDescription(TypeAssertion typeAssertion) { + return typeAssertion.text() + "\n in: " + typeAssertion.fileName(); + } + + + private String toText(List expectedRels) { + StringJoiner joiner = new StringJoiner("\n// ", "// ", ""); + for (String rel : expectedRels) { + joiner.add(rel); + } + return joiner.toString(); + } + + private List getSubtypeRels(String sourceFilePath) { + BLangPackage pkgNode = BCompileUtil.compileSemType(sourceFilePath).bLangPackage; + // xxxx-e.bal pattern is used to test bal files where jBallerina type checking doesn't support type operations + // such as intersection. Make sure not to use nBallerina type negation (!) with this as jBallerina compiler + // front end doesn't generate AST from those. + if (!sourceFilePath.endsWith("-e.bal")) { + ensureNoErrors(pkgNode); + } + + List typeAndClassDefs = new ArrayList<>(); + typeAndClassDefs.addAll(pkgNode.constants); + typeAndClassDefs.addAll(pkgNode.typeDefinitions); + + SemTypeResolver typeResolver = new CompilerSemTypeResolver(); + TypeTestContext typeCheckContext = + ComplierTypeTestContext.from(Context.from(pkgNode.semtypeEnv)); + typeResolver.defineSemTypes(typeAndClassDefs, typeCheckContext); + Map typeMap = pkgNode.semtypeEnv.getTypeNameSemTypeMap(); + TypeTestAPI api = CompilerTypeTestAPI.getInstance(); + List subtypeRelations = new ArrayList<>(); + List typeNameList = typeMap.keySet().stream() + .filter(n -> !n.startsWith("$anon")) + .sorted(SemTypeTest::ballerinaStringCompare) + .toList(); + int size = typeNameList.size(); + for (int i = 0; i < size; i++) { + for (int j = i + 1; j < size; j++) { + String name1 = typeNameList.get(i); + String name2 = typeNameList.get(j); + + SemType t1 = typeMap.get(name1); + SemType t2 = typeMap.get(name2); + if (api.isSubtype(typeCheckContext, t1, t2)) { + subtypeRelations.add(TypeRel.rel(name1, name2)); + } + if (api.isSubtype(typeCheckContext, t2, t1)) { + subtypeRelations.add(TypeRel.rel(name2, name1)); + } + } + } + + return subtypeRelations.stream() + .map(TypeRel::toString) + .sorted(SemTypeTest::ballerinaStringCompare) + .collect(Collectors.toList()); + } + + private void ensureNoErrors(BLangPackage bLangPackage) { + List errors = bLangPackage.getDiagnostics().stream() + .filter(d -> d.diagnosticInfo().severity() == DiagnosticSeverity.ERROR) + .toList(); + if (!errors.isEmpty()) { + Assert.fail(errors.stream() + .map(Diagnostic::toString) + .reduce("", (a, b) -> a + "\n" + b)); + } + } + + private static int ballerinaStringCompare(String o1, String o2) { + return ValueComparisonUtils.compareValues(StringUtils.fromString(o1), StringUtils.fromString(o2), ""); + } + + List extractSubtypeRelations(String fileName) { + try { + Path path = resolvePath(fileName); + Stream lines = Files.lines(Path.of(path.toString())); + return lines.filter(s -> s.startsWith("// ") && s.contains("<:")) + .map(s -> s.substring(3).strip()) + .sorted() + .collect(Collectors.toList()); + } catch (IOException e) { + Assert.fail(e.toString()); + } + return null; + } + + List extractVarTypes(String fileName) { + try { + Path path = resolvePath(fileName); + Stream lines = Files.lines(Path.of(path.toString())); + return lines.filter(s -> s.startsWith("// ")) + .map(s -> s.substring(3).replaceAll("\\s", "").split("=")) + .collect(Collectors.toList()); + } catch (IOException e) { + Assert.fail(e.toString()); + } + return null; + } + + private Path resolvePath(String fileName) { + return Paths.get("src/test/resources").resolve(fileName); + } + + /** + * Represent subtype relationship. + * + * @param subType subtype name + * @param superType super type name + * @since 2201.10.0 + */ + private record TypeRel(String subType, String superType) { + + public static TypeRel rel(String sub, String sup) { + return new TypeRel(sub, sup); + } + + @Override + public String toString() { + return subType + "<:" + superType; + } + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeAssertion.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeAssertion.java new file mode 100644 index 000000000000..e4452711b6c1 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeAssertion.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +import java.nio.file.Paths; + +/** + * Subtype test. + * + * @param Which semtype (runtime or compiler) is used for type assertion. + * @param context Type context under which {@code SemTypes} were defined. + * @param fileName Name of the file in which types were defined in. + * @param lhs Resolved {@code SemType} for the Left-hand side of the subtype test. + * @param rhs Resolved {@code SemType} for the Right-hand side of the subtype test. + * @param kind Relationship between the two types. + * @param text Text that will be shown in case of assertion failure. + * @since 3.0.0 + */ +public record TypeAssertion(TypeTestContext context, String fileName, SemType lhs, SemType rhs, + SemTypeAssertionTransformer.RelKind kind, String text) { + + public TypeAssertion { + if (kind != null) { + assert lhs != null; + assert rhs != null; + } + } + + @Override + public String toString() { + return Paths.get(fileName).getFileName().toString() + ": " + text; + } +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestAPI.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestAPI.java new file mode 100644 index 000000000000..60dc1190abfb --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestAPI.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +public interface TypeTestAPI { + + boolean isSubtype(TypeTestContext cx, SemType t1, SemType t2); + + boolean isSubtypeSimple(SemType t1, SemType t2); + + boolean isListType(SemType t); + + boolean isMapType(SemType t); + + SemType intConst(long l); + + SemType mappingMemberTypeInnerVal(TypeTestContext context, SemType type, SemType m); + + SemType listProj(TypeTestContext context, SemType t, SemType key); + + SemType listMemberType(TypeTestContext context, SemType t, SemType key); +} diff --git a/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContext.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContext.java new file mode 100644 index 000000000000..86402244cc03 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestContext.java @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). + * + * WSO2 LLC. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package io.ballerina.semtype.port.test; + +public interface TypeTestContext { + + TypeTestEnv getEnv(); + + Object getInnerEnv(); + + Object getInnerContext(); +} diff --git a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/BooleanSubtype.java b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java similarity index 60% rename from semtypes/src/main/java/io/ballerina/semtype/subtypedata/BooleanSubtype.java rename to tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java index d4b9ede5829e..3a1afd44326f 100644 --- a/semtypes/src/main/java/io/ballerina/semtype/subtypedata/BooleanSubtype.java +++ b/tests/jballerina-semtype-port-test/src/test/java/io/ballerina/semtype/port/test/TypeTestEnv.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2024, WSO2 LLC. (http://www.wso2.com). * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache License, * Version 2.0 (the "License"); you may not use this file except * in compliance with the License. * You may obtain a copy of the License at @@ -15,15 +15,16 @@ * specific language governing permissions and limitations * under the License. */ -package io.ballerina.semtype.subtypedata; -import io.ballerina.semtype.ProperSubtypeData; +package io.ballerina.semtype.port.test; -/** - * Represent BooleanSubtype. - * - * @since 2.0.0 - */ -public class BooleanSubtype implements ProperSubtypeData { +import java.util.Map; + +public interface TypeTestEnv { + + Map getTypeNameSemTypeMap(); + + void addTypeDef(String value, SemType semtype); + Object getInnerEnv(); } diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/README.md b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/README.md new file mode 100644 index 000000000000..1a832baa8cd3 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/README.md @@ -0,0 +1,6 @@ +Note: +===== +`-e.bal` files in this test folder does not mean what it means in nBallerina repo. In this folder it means the +tests should run even though jBallerina compiler frontend emit errors. +This was necessary as jBallerina emit errors for some valid type constructs such as empty tuple. +We need to make sure that type negation operator (!) is not used in these `-e.bal` tests. \ No newline at end of file diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/anydata.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/anydata.bal new file mode 100644 index 000000000000..d80f571250bb --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/anydata.bal @@ -0,0 +1,8 @@ +import ballerina/lang.regexp; + +// DataType<:Anydata +// Anydata<:DataType + +type Anydata anydata; + +type DataType ()|boolean|int|float|decimal|string|xml|regexp:RegExp|DataType[]|map|table>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/basic.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/basic.bal new file mode 100644 index 000000000000..147ee10e2db9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/basic.bal @@ -0,0 +1,19 @@ +// Boolean<:Any +// Decimal<:Any +// Float<:Any +// Handle<:Any +// Int<:Any +// Nil<:Any +// String<:Any +type Nil (); +type String string; +type Boolean boolean; +type Int int; +type Float float; +type Decimal decimal; +type Error error; +type Handle handle; +type Any any; + + + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/bdddiff1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/bdddiff1.bal new file mode 100644 index 000000000000..48ee7543c899 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/bdddiff1.bal @@ -0,0 +1,9 @@ +// IA<:INA +// IA<:U + +// INA<:U +// U<:INA + +type INA int?[]; +type IA int[]; +type U IA|INA; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/boolean-subtype.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/boolean-subtype.bal new file mode 100644 index 000000000000..438c2f94a61b --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/boolean-subtype.bal @@ -0,0 +1,10 @@ +// B<:TF +// F<:B +// F<:TF +// T<:B +// T<:TF +// TF<:B +type T true; +type F false; +type TF true|false; +type B boolean; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/contextual.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/contextual.bal new file mode 100644 index 000000000000..7782b2311a75 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/contextual.bal @@ -0,0 +1,14 @@ +// F<:F_TY +// F_TY<:F +// I<:I_TY +// I<:ZERO_OR_ONE +// I_TY<:I +// I_TY<:ZERO_OR_ONE + +const int I_TY = 1; +const float F_TY = 1; + +const I = 1; +const F = 1f; + +type ZERO_OR_ONE 0|1; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton.bal new file mode 100644 index 000000000000..9ca8da408cbe --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton.bal @@ -0,0 +1,18 @@ +// Zero<:DECIMAL +// One<:DECIMAL +// Minus<:DECIMAL +// ZO<:DECIMAL +// ZOM<:DECIMAL +// ZO<:ZOM +// Zero<:ZO +// One<:ZO +// Zero<:ZOM +// One<:ZOM +// Minus<:ZOM + +type Zero 0d; +type One 1d; +type Minus -1d; +type ZO Zero|One; +type ZOM Zero|One|Minus; +type DECIMAL decimal; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton2.bal new file mode 100644 index 000000000000..a80c04032552 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/decimal-singleton2.bal @@ -0,0 +1,14 @@ +// Zero<:DECIMAL +// MinusZero<:DECIMAL +// One<:DECIMAL +// FpOne<:DECIMAL +// Zero<:MinusZero +// MinusZero<:Zero +// One<:FpOne +// FpOne<:One + +type Zero 0d; +type MinusZero -0d; +type One 1d; +type FpOne 1.0d; +type DECIMAL decimal; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/distinct-error.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/distinct-error.bal new file mode 100644 index 000000000000..aa40d551a92f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/distinct-error.bal @@ -0,0 +1,21 @@ +// E1<:E +// E2<:E +// E2<:E4 +// E3<:E +// E3<:E1 +// E3<:E2 +// E3<:E4 +// E4<:E +// E4<:E2 + +type E error; + +type E1 distinct error; + +type E2 error; + +type E3 E1 & E2; + +type E4 error; + +type R1 record {}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error1.bal new file mode 100644 index 000000000000..5ae9ffb96d91 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error1.bal @@ -0,0 +1,9 @@ +// EL<:E +// ER1<:E +// ER1<:ER2 +// ER2<:E +// ER2<:ER1 +type EL error; +type ER1 error; +type ER2 error; +type E error; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error2.bal new file mode 100644 index 000000000000..3cd76b901d0c --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/error2.bal @@ -0,0 +1,14 @@ +// E<:Cloneable +// E<:EM +// E<:ER +// EM<:Cloneable +// EM<:E +// EM<:ER +// ER<:Cloneable +// ER<:E +// ER<:EM +type E error; +type ER error>; +type Cloneable readonly|xml|Cloneable[]|map; +type EM error>; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-2-e.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-2-e.bal new file mode 100644 index 000000000000..54d71b2f1e78 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-2-e.bal @@ -0,0 +1,15 @@ +// EMPTY<:ALL +// EMPTY<:IntArray +// IS1<:ALL +// IS2<:ALL +// IS3<:ALL +// IntArray<:ALL + +type IntArray int[]; +type IS int|string; +type EMPTY []; +type IS1 IS[1]; +type IS2 IS[2]; +type IS3 IS[3]; + +type ALL EMPTY|IS1|IS2|IS3|[IS, IS, IS, IS, IS...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-3-e.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-3-e.bal new file mode 100644 index 000000000000..3a468cf411e8 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-3-e.bal @@ -0,0 +1,42 @@ +// EMPTY<:ISArray +// EMPTY<:U +// EMPTY<:V +// EMPTY<:W +// EMPTY<:X +// EMPTY<:Y +// EMPTY<:Z +// ISArray<:U +// ISArray<:V +// ISArray<:W +// ISArray<:X +// ISArray<:Y +// ISArray<:Z +// X<:Y +// X<:Z +// Y<:X +// Y<:Z +// Z<:X +// Z<:Y + + +type IS int|string; +type EMPTY []; +type ISArray IS[]; + +// @type ISArray < U +type U EMPTY|[IS|float, IS...]; + +// @type ISArray < V +type V EMPTY|[IS]|[IS, IS|float, IS...]; + +// @type ISArray < W +type W EMPTY|[IS]|[IS, IS|float]|[IS, IS, IS|float, IS...]; + +// @type ISArray < X +type X EMPTY|[IS]|[IS, IS|float]|[IS, IS, IS|float]|[IS, IS, IS, IS|float, IS...]; + +// @type ISArray < Y +type Y EMPTY|[IS, IS, IS, IS|float, IS...]|[IS, IS, IS|float]|[IS, IS|float]|[IS]; + +// @type ISArray < Z +type Z [IS, IS, IS, IS|float, IS...]|[IS, IS, IS|float]|[IS, IS|float]|[IS]|EMPTY; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-4-e.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-4-e.bal new file mode 100644 index 000000000000..02c4649d8958 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2-4-e.bal @@ -0,0 +1,44 @@ +// EMPTY<:IntArray +// EMPTY<:P +// EMPTY<:Q +// EMPTY<:R +// EMPTY<:S +// EMPTY<:T +// EMPTY<:T1 +// IntArray<:P +// IntArray<:Q +// IntArray<:R +// IntArray<:S +// IntArray<:T +// IntArray<:T1 +// P<:Q +// P<:T +// P<:T1 +// R<:Q +// S<:Q +// S<:R +// S<:T1 +// T1<:Q +// T<:Q + +type IntArray int[]; +type IS int|string; +type EMPTY []; + +// @type IntArray < P +type P EMPTY|[int]|[IS, int]|[IS, IS, IS, IS...]; + +// @type IntArray < Q +type Q EMPTY|[int]|[IS, IS]|[IS, IS, IS|float, IS...]; + +// @type IntArray < R +type R EMPTY|[int]|[IS, IS]|[int, int, IS|float, IS...]; + +// @type IntArray < S +type S EMPTY|[int]|[IS, IS]|[int, int, int, IS...]; + +// @type IntArray < T +type T EMPTY|[int]|[IS, int]|[IS, IS, IS|float, IS...]; + +// @type IntArray < T1 +type T1 EMPTY|[int]|[IS, IS, string...]|[IS, IS, IS, IS...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2.bal new file mode 100644 index 000000000000..8cf15c770a8f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-2.bal @@ -0,0 +1,17 @@ +// LargeArray<:IntArray +// LargeArray2<:IntArray + +public const int MAX_VALUE = 2147483637; +public const int MAX_VALUE_M_1 = 2147483636; + +type IntArray int[]; + +// @type LargeArray < IntArray +type LargeArray int[MAX_VALUE]; + +// @type LargeArray2 < IntArray +// @type LargeArray <> LargeArray2 +type LargeArray2 int[MAX_VALUE_M_1]; + +// @type Int5Intersection = Int5 +//type Int5Intersection int[5] & !LargeArray; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-tuple.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-tuple.bal new file mode 100644 index 000000000000..df4bd385f125 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array-tuple.bal @@ -0,0 +1,23 @@ +// Int1<:IntT +// Int2<:IntIntT +// Int2R<:Int2 +// Int2R<:IntIntRT +// Int2R<:IntIntT +// IntIntRT<:Int2 +// IntIntRT<:Int2R +// IntIntRT<:IntIntT +// IntIntT<:Int2 +// IntT<:Int1 + +type Int1 int[1]; +type Int2 int[2]; + +type IntT [int]; + +type IntIntT [int, int]; + +type IntIntRT readonly & [int, int]; + +type Int2R readonly & int[2]; + +type Int int; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array.bal new file mode 100644 index 000000000000..a8cecd384acc --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/fixed-length-array.bal @@ -0,0 +1,93 @@ +// Array2OfInt5<:ArrayOfInt5 +// Array2OfInt5<:ArrayOfIntArray +// Array2OfInt5<:ArrayOfIntFive +// Array5OfInt5<:Array5OfIntArray +// Array5OfInt5<:ArrayFiveOfIntFive +// Array5OfInt5<:ArrayOfInt5 +// Array5OfInt5<:ArrayOfIntArray +// Array5OfInt5<:ArrayOfIntFive +// Array5OfIntArray<:ArrayOfIntArray +// Array7OfArray2OfInt5<:Array7x2x5 +// Array7x2x5<:Array7OfArray2OfInt5 +// ArrayFiveOfIntFive<:Array5OfInt5 +// ArrayFiveOfIntFive<:Array5OfIntArray +// ArrayFiveOfIntFive<:ArrayOfInt5 +// ArrayFiveOfIntFive<:ArrayOfIntArray +// ArrayFiveOfIntFive<:ArrayOfIntFive +// ArrayOfInt5<:ArrayOfIntArray +// ArrayOfInt5<:ArrayOfIntFive +// ArrayOfIntFive<:ArrayOfInt5 +// ArrayOfIntFive<:ArrayOfIntArray +// EmptyIntArray<:ArrayOfInt5 +// EmptyIntArray<:ArrayOfIntArray +// EmptyIntArray<:ArrayOfIntFive +// EmptyIntArray<:IntArray +// FIVE<:INT +// Int5<:IntArray +// N<:Array2OfInt5 +// N<:Array5OfInt5 +// N<:Array5OfIntArray +// N<:Array7OfArray2OfInt5 +// N<:Array7x2x5 +// N<:ArrayFiveOfIntFive +// N<:ArrayOfInt5 +// N<:ArrayOfIntArray +// N<:ArrayOfIntFive +// N<:EmptyIntArray +// N<:FIVE +// N<:INT +// N<:Int5 +// N<:IntArray +// N<:ROArrayFiveOfIntFive +// N<:ROInt5 +// N<:ROIntArray +// N<:TwoArraysOfInt5 +// ROArrayFiveOfIntFive<:Array5OfInt5 +// ROArrayFiveOfIntFive<:Array5OfIntArray +// ROArrayFiveOfIntFive<:ArrayFiveOfIntFive +// ROArrayFiveOfIntFive<:ArrayOfInt5 +// ROArrayFiveOfIntFive<:ArrayOfIntArray +// ROArrayFiveOfIntFive<:ArrayOfIntFive +// ROInt5<:Int5 +// ROInt5<:IntArray +// ROInt5<:ROIntArray +// ROIntArray<:IntArray +// EmptyIntArray<:ROIntArray + +type IntArray int[]; + +type Int5 int[5]; + +type ArrayOfIntArray int[][]; + +type ArrayOfInt5 int[][5]; + +type Array5OfInt5 int[5][5]; + +type INT int; + +type Array5OfIntArray int[5][]; + +type ROIntArray readonly & IntArray; + +type ROInt5 readonly & int[5]; + +const FIVE = 5; + +type ArrayOfIntFive int[][FIVE]; + +type ArrayFiveOfIntFive int[FIVE][FIVE]; + +type ROArrayFiveOfIntFive ArrayFiveOfIntFive & readonly; + +type N never; + +type TwoArraysOfInt5 int[2][][5]; + +type EmptyIntArray int[0]; + +type Array2OfInt5 Int5[2]; + +type Array7OfArray2OfInt5 Array2OfInt5[7]; + +type Array7x2x5 int[7][2][5]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton.bal new file mode 100644 index 000000000000..7a28045e105e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton.bal @@ -0,0 +1,25 @@ +// Z<:F +// Z<:ZO +// Z<:ZOT +// Z<:ZT +// ZO<:F +// ZO<:ZOT +// ZOT<:F +// ZT<:F +// ZT<:ZOT +// O<:F +// O<:ZO +// O<:ZOT +// T<:F +// T<:ZOT +// T<:ZT + +type Z 0.0; +type O 1.0; +type T -2.0; + +type ZOT Z|O|T; +type ZO Z|O; +type ZT Z|T; + +type F float; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton2.bal new file mode 100644 index 000000000000..3f404c273992 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/float-singleton2.bal @@ -0,0 +1,20 @@ +// ALSO_NAN<:Float +// ALSO_NAN<:NAN +// INFINITY<:Float +// NAN<:ALSO_NAN +// NAN<:Float +// NEGATIVE_INFINITY<:Float +// NegativeZero<:Float +// NegativeZero<:Zero +// Zero<:Float +// Zero<:NegativeZero + +const INFINITY = 1.0/0.0; +const NEGATIVE_INFINITY = -1.0/0.0; +const NAN = 0f/0f; +const ALSO_NAN = -NAN; + +type Zero 0.0; +type NegativeZero -0.0; + +type Float float; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/function.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/function.bal new file mode 100644 index 000000000000..c1657b0ca2cc --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/function.bal @@ -0,0 +1,10 @@ +// F<:A +// S<:T + +type F function() returns F; +type A function() returns any|error; + +type S function(int?) returns string; +type T function(int) returns string?; + + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/future-subtype.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/future-subtype.bal new file mode 100644 index 000000000000..20f552629616 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/future-subtype.bal @@ -0,0 +1,10 @@ +// I<:U1 +// I<:U2 +// S<:U1 +// S<:U2 +// U1<:U2 + +type I future; +type S future; +type U1 I|S; +type U2 future; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/hard.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/hard.bal new file mode 100644 index 000000000000..d64f6c812c45 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/hard.bal @@ -0,0 +1,11 @@ +// J1<:J2 +// J1<:J3 +// J2<:J1 +// J2<:J3 +// J3<:J1 +// J3<:J2 +type J1 [J2,J1]|record {| J1 x; J1 y; |}|()|int|string; +type J2 ()|int|string|[J1,J2]|record {| J2 x; J1 y; |}; +type J3 ()|int|string|[J3,J3]|record {| J3 x; J3 y; |}; + + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton.bal new file mode 100644 index 000000000000..12e49cac6304 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton.bal @@ -0,0 +1,13 @@ +// INT_MIN<:Int +// ONE<:Int +// ONE<:ZERO_OR_ONE +// ZERO<:Int +// ZERO<:ZERO_OR_ONE +// ZERO_OR_ONE<:Int +const ONE = 1; +const ZERO = 0; +const INT_MIN = -9223372036854775807 - 1; + +type ZERO_OR_ONE ZERO|ONE; +type Int int; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton2.bal new file mode 100644 index 000000000000..c5d713c07693 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-singleton2.bal @@ -0,0 +1,7 @@ +// MinusOne<:Sign +// One<:Byte +// One<:Sign +type One 1; +type MinusOne -1; +type Sign One|MinusOne; +type Byte byte; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-subtype.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-subtype.bal new file mode 100644 index 000000000000..bc5fa06625d9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/int-subtype.bal @@ -0,0 +1,26 @@ +// Byte<:Sint16 +// Byte<:Sint32 +// Byte<:Uint16 +// Byte<:Uint32 +// Byte<:Uint8 +// N33000<:Sint32 +// N33000<:Uint16 +// N33000<:Uint32 +// Sint16<:Sint32 +// Sint8<:Sint16 +// Sint8<:Sint32 +// Uint16<:Sint32 +// Uint16<:Uint32 +// Uint8<:Byte +// Uint8<:Sint16 +// Uint8<:Sint32 +// Uint8<:Uint16 +// Uint8<:Uint32 +type Byte byte; +type Uint8 int:Unsigned8; +type Uint16 int:Unsigned16; +type Uint32 int:Unsigned32; +type Sint8 int:Signed8; +type Sint16 int:Signed16; +type Sint32 int:Signed32; +const N33000 = 33000; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/list-fixed.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/list-fixed.bal new file mode 100644 index 000000000000..eb030abfabfc --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/list-fixed.bal @@ -0,0 +1,8 @@ +// IF<:I +// IF<:IFF +// IFF<:I +// IFF<:IF + +type I int[]; +type IF int[12224]; +type IFF int[12224]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping1.bal new file mode 100644 index 000000000000..e07ea23f6ec2 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping1.bal @@ -0,0 +1,19 @@ +// M1<:M +// M1<:M2 +// M1<:N +// M2<:M +// M2<:M1 +// M2<:N +// M<:M1 +// M<:M2 +// M<:N +// N<:M +// N<:M1 +// N<:M2 +type M map; + +type N map; + +type M1 map|record {}; + +type M2 map|record {}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping2.bal new file mode 100644 index 000000000000..53dec4dc4440 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mapping2.bal @@ -0,0 +1,19 @@ +// M1<:M +// M1<:M2 +// M1<:N +// M2<:M +// M2<:M1 +// M2<:N +// M<:M1 +// M<:M2 +// M<:N +// N<:M +// N<:M1 +// N<:M2 +type M map<(1|2|3)>; + +type N map<(1|2|3)>; + +type M1 M|map<(1|2)>; + +type M2 N|map<(1|2)>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mutable-record1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mutable-record1.bal new file mode 100644 index 000000000000..3cc12b9c5b3d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/mutable-record1.bal @@ -0,0 +1,36 @@ +// NN<:U +// NN<:UU +// NS<:U +// NS<:UU +// SN<:U +// SN<:UU +// SS<:U +// SS<:UU +// U<:UU + +type NN record {| + int x; + int y; +|}; + +type SS record {| + string x; + string y; +|}; + +type NS record {| + int x; + string y; +|}; + +type SN record {| + string x; + int y; +|}; + +type UU record {| + int|string x; + int|string y; +|}; + +type U NN|SS|NS|SN; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/never.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/never.bal new file mode 100644 index 000000000000..2516326d50c9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/never.bal @@ -0,0 +1,21 @@ +// Fun<:FunOrNever +// FunOrNever<:Fun +// Int<:IntOrNever +// IntOrNever<:Int +// Key<:KeyOrNever +// KeyOrNever<:Key +// Never<:Fun +// Never<:FunOrNever +// Never<:Int +// Never<:IntOrNever +// Never<:Key +// Never<:KeyOrNever +type Never never; +type Int int; +type Fun function(int); +type FunOrNever Fun|never; +type IntOrNever Int|never; +type Key "key"; +type KeyOrNever Key|never; + + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly1.bal new file mode 100644 index 000000000000..0f841c7862de --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly1.bal @@ -0,0 +1,19 @@ +// Boolean<:RO +// Decimal<:RO +// Error<:RO +// Float<:RO +// Handle<:RO +// Int<:RO +// Nil<:RO +// String<:RO +type RO readonly; +type Nil (); +type String string; +type Boolean boolean; +type Int int; +type Float float; +type Decimal decimal; +type Error error; +type Handle handle; +type List (any|error)[]; +type Mapping map; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly2.bal new file mode 100644 index 000000000000..39142d87a52b --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/readonly2.bal @@ -0,0 +1,26 @@ +// E<:ER +// ER<:E +// IR<:Int +// Int<:IR +// LR1<:LR +// LR1<:LR2 +// LR2<:LR +// LR2<:LR1 +// MR1<:MR +// MR1<:MR2 +// MR2<:MR +// MR2<:MR1 +type E error; +type ER error>; +type LR readonly[]; +type MR map; +// These two should be equivalent +type MR1 readonly & map; +type MR2 readonly & map; +// As should these +type LR1 readonly & (any|error)[]; +type LR2 readonly & readonly[]; +// As should these +type IR int & readonly; +type Int int; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-recursive.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-recursive.bal new file mode 100644 index 000000000000..bb595595ecaa --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-recursive.bal @@ -0,0 +1,8 @@ +// SR<:S +type SR stream; + +type S stream; + +// S1<:SR +// S1<:S +type S1 stream<()>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype.bal new file mode 100644 index 000000000000..372455449317 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype.bal @@ -0,0 +1,10 @@ +// I<:U1 +// I<:U2 +// S<:U1 +// S<:U2 +// U1<:U2 + +type I stream; +type S stream; +type U1 I|S; +type U2 stream; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype2.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype2.bal new file mode 100644 index 000000000000..a170dd69ffe3 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/stream-subtype2.bal @@ -0,0 +1,20 @@ +// I<:J +// I<:J1 +// I<:J2 +type I stream; + +// S<:J +// S<:J1 +// S<:J2 +type S stream; + + +// J<:J1 +// J1<:J +type J stream; +type J1 stream; + +// J<:J2 +// J1<:J2 +type J2 stream; +type J3 stream; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-all-subtypes.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-all-subtypes.bal new file mode 100644 index 000000000000..33ca5ed91c29 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-all-subtypes.bal @@ -0,0 +1,29 @@ +// A<:Bar +// A<:Baz +// A<:C +// A<:S +// A<:SC +// Bar<:Baz +// Bar<:S +// Bar<:SC +// Baz<:S +// Baz<:SC +// C<:Baz +// C<:S +// C<:SC +// Foo<:Bar +// Foo<:Baz +// Foo<:S +// Foo<:SC +// S<:Baz +// S<:SC +// SC<:Baz +// SC<:S + +type S string; +type C string:Char; +type A "A"; +type Foo "Foo"; +type Bar A|Foo; +type Baz Bar|S|C; +type SC S|C; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-char.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-char.bal new file mode 100644 index 000000000000..b487d618849f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-char.bal @@ -0,0 +1,13 @@ +// C1<:C +// C1<:D +// C1<:S +// C<:C1 +// C<:D +// C<:S +// D<:C +// D<:C1 +// D<:S +type C string:Char; +type C1 string:Char; +type D C|C1; +type S string; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton-same-shape.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton-same-shape.bal new file mode 100644 index 000000000000..681bc54ca07a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton-same-shape.bal @@ -0,0 +1,27 @@ +// WHY<:S +// WHY<:Y +// WHY<:Y1 +// X1<:S +// X1<:X +// X1<:XES +// X2<:S +// X2<:XES +// X<:S +// X<:X1 +// X<:XES +// XES<:S +// Y1<:S +// Y1<:WHY +// Y1<:Y +// Y<:S +// Y<:WHY +// Y<:Y1 + +type X "X"; +type X1 "X"; +type X2 "x"; +type Y "Why"; +type Y1 "Why"; +type XES X|X1|X2; +type WHY Y|Y1; +type S string; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton.bal new file mode 100644 index 000000000000..b23a064fca77 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/string-singleton.bal @@ -0,0 +1,24 @@ +// X<:S +// X<:XY +// X<:XYZ +// X<:XZ +// XY<:S +// XY<:XYZ +// XYZ<:S +// XZ<:S +// XZ<:XYZ +// Y<:S +// Y<:XY +// Y<:XYZ +// Z<:S +// Z<:XYZ +// Z<:XZ +type X "x"; +type Y "y-y"; +type Z "z-z"; + +type XYZ X|Y|Z; +type XY X|Y; +type XZ X|Z; + +type S string; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/table-key.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/table-key.bal new file mode 100644 index 000000000000..108808ce389b --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/table-key.bal @@ -0,0 +1,30 @@ +type R record {| + readonly [string, string] name; + readonly string department; + readonly string city; + int salary; +|}; + +type KS1 table key(name); + +type KS2 table key(city); + +type KS3 table key(department); + +type KS4 table key(city, department); + +type KS5 table key(department, city); + +type KS6 table key(name, department, city); + +// KS2<:KC1 +// KS3<:KC1 +type KC1 table key; + +// KS1<:KC2 +// KS4<:KC2 +// KS5<:KC2 +type KC2 table key<[string, string]>; + +// KS6<:KC3 +type KC3 table key<[[string, string], string, string]>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple1.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple1.bal new file mode 100644 index 000000000000..3b43eec0ab60 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple1.bal @@ -0,0 +1,6 @@ +// T1<:T2 +// T4<:T3 +type T1 [int]; +type T2 [int?]; +type T3 [int, int?]; +type T4 [int, int] | [int, ()]; \ No newline at end of file diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple4.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple4.bal new file mode 100644 index 000000000000..ecb6410c704e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/tuple4.bal @@ -0,0 +1,11 @@ +// T2<:T1 +// T2<:T4 +// T3<:T1 +// T3<:T2 +// T3<:T4 + +type T1 [int...]; +type T2 [int,int...]; + +type T4 [int?,int?...]; +type T3 [int, int, int, int...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/typedesc-subtype.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/typedesc-subtype.bal new file mode 100644 index 000000000000..64b30d899d46 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/typedesc-subtype.bal @@ -0,0 +1,10 @@ +// I<:U1 +// I<:U2 +// S<:U1 +// S<:U2 +// U1<:U2 + +type I typedesc; +type S typedesc; +type U1 I|S; +type U2 typedesc; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-never.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-never.bal new file mode 100644 index 000000000000..d8054d1fcecd --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-never.bal @@ -0,0 +1,34 @@ +// C<:CS +// E<:ENS +// E<:ES +// ENS<:ES +// ES<:ENS +// N<:CS +// N<:ENS +// N<:ES +// N<:NS +// N<:PS +// N<:T +// N<:TS +// NS<:CS +// NS<:ENS +// NS<:ES +// NS<:N +// NS<:PS +// NS<:T +// NS<:TS +// P<:PS +// T<:TS +// TS<:T + +type N xml; +type NS xml; +type E xml:Element; +type T xml:Text; +type C xml:Comment; +type P xml:ProcessingInstruction; +type ES xml; +type CS xml; +type TS xml; +type PS xml; +type ENS xml; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-sequence.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-sequence.bal new file mode 100644 index 000000000000..ccb734bfffd4 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-sequence.bal @@ -0,0 +1,90 @@ +// E<:P +// E<:Q +// E<:U +// E<:V +// E<:X +// E<:XE +// E<:XEU +// E<:Y +// N<:P +// N<:Q +// N<:T +// N<:U +// N<:V +// N<:X +// N<:XE +// N<:XEU +// N<:XT +// N<:Y +// P<:Q +// P<:X +// P<:XE +// P<:XEU +// P<:Y +// Q<:P +// Q<:X +// Q<:XE +// Q<:XEU +// Q<:Y +// T<:P +// T<:Q +// T<:V +// T<:X +// T<:XE +// T<:XEU +// T<:XT +// T<:Y +// U<:P +// U<:Q +// U<:V +// U<:X +// U<:XE +// U<:XEU +// U<:Y +// V<:P +// V<:Q +// V<:X +// V<:XE +// V<:XEU +// V<:Y +// X<:P +// X<:Q +// X<:XE +// X<:XEU +// X<:Y +// XE<:P +// XE<:Q +// XE<:X +// XE<:XEU +// XE<:Y +// XEU<:P +// XEU<:Q +// XEU<:X +// XEU<:XE +// XEU<:Y +// XT<:P +// XT<:Q +// XT<:T +// XT<:V +// XT<:X +// XT<:XE +// XT<:XEU +// XT<:Y +// Y<:P +// Y<:Q +// Y<:X +// Y<:XE +// Y<:XEU + +type X xml; +type Y xml; +type P xml; +type Q xml

; +type U xml; +type V xml; +type N xml; +type E xml:Element; +type XE xml; +type XEU xml|E; +type T xml:Text; +type XT xml; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-singleton.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-singleton.bal new file mode 100644 index 000000000000..ca48dee0cd10 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml-singleton.bal @@ -0,0 +1,13 @@ +// C<:CTE +// C<:X +// CTE<:X +// E<:CTE +// E<:X +// T<:CTE +// T<:X + +type C xml:Comment; +type E xml:Element; +type CTE C|T|E; +type T xml:Text; +type X xml; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml.bal new file mode 100644 index 000000000000..5b347b8ccea5 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/data/xml.bal @@ -0,0 +1,13 @@ +// C<:EC +// C<:X +// E<:EC +// E<:ET +// E<:X +// EC<:X +// ET<:X + +type E xml; +type C xml; +type EC xml; +type ET xml; +type X xml; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/fixed-length-array-too-large-te.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/fixed-length-array-too-large-te.bal new file mode 100644 index 000000000000..6b25f6b2e889 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/fixed-length-array-too-large-te.bal @@ -0,0 +1,2 @@ +public const int MAX_VALUE = 9223372036854775807; +type LargeArray int[MAX_VALUE]; // @error diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/localVar/func.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/localVar/func.bal new file mode 100644 index 000000000000..a4511d0bca26 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/localVar/func.bal @@ -0,0 +1,11 @@ +// x = R +// y = S + +type N int; +type S string; +type R N|S; + +function foo() { + R x = 1; + S y = "str"; +} diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/float-altered.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/float-altered.bal new file mode 100644 index 000000000000..985013bd1e1f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/float-altered.bal @@ -0,0 +1,9 @@ +// NegativeZero<:Float +// NegativeZero<:Zero +// Zero<:Float +// Zero<:NegativeZero + +type Zero 0.0; +type NegativeZero -0.0; + +type Float float; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/function-altered.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/function-altered.bal new file mode 100644 index 000000000000..016125fd386a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/function-altered.bal @@ -0,0 +1,8 @@ +// F<:A +// S<:T + +type F function() returns S; +type A function() returns any; + +type S function(int?) returns string; +type T function(int) returns string?; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/int-singleton-altered.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/int-singleton-altered.bal new file mode 100644 index 000000000000..75faa34ff975 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/int-singleton-altered.bal @@ -0,0 +1,13 @@ +// INT_MIN<:Int +// ONE<:Int +// ONE<:ZERO_OR_ONE +// ZERO<:Int +// ZERO<:ZERO_OR_ONE +// ZERO_OR_ONE<:Int + +const ONE = 1; +const ZERO = 0; +const int INT_MIN = -9223372036854775807 - 1; + +type ZERO_OR_ONE ZERO|ONE; +type Int int; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/list-type-test.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/list-type-test.bal new file mode 100644 index 000000000000..62d962464787 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/list-type-test.bal @@ -0,0 +1,19 @@ +// IIT<:IRIT +// IIT<:L +// IIT<:L1 +// IIT<:RIT +// IRIT<:L +// IRIT<:L1 +// IRIT<:RIT +// L1<:L +// L1<:RIT +// L<:L1 +// L<:RIT +// RIT<:L +// RIT<:L1 + +type L int[]; +type L1 int[]; +type RIT [int...]; +type IRIT [int, int...]; +type IIT [int, int]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/map-type-test.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/map-type-test.bal new file mode 100644 index 000000000000..f0de0af00861 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/map-type-test.bal @@ -0,0 +1,11 @@ +// MB<:MI +// MB<:RIC +// MI<:RIC +// RC<:MI +// RC<:RIC +// RIC<:MI + +type MI map; +type MB map; +type RIC record {| int...; |}; +type RC record {| int i; |}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/type-test.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/type-test.bal new file mode 100644 index 000000000000..dadcd0eaaca4 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/simple-type/type-test.bal @@ -0,0 +1,11 @@ +// N<:R +// N<:T +// R<:N +// R<:T +// T<:N +// T<:R + +type T int; +type T1 string; +type N int; +type R N; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel-wrong.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel-wrong.bal new file mode 100644 index 000000000000..0dfe7e74972a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel-wrong.bal @@ -0,0 +1,4 @@ +type X int; + +// Y <: X +type Y byte; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/anydata-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/anydata-tv.bal new file mode 100644 index 000000000000..f0eb6b30bc68 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/anydata-tv.bal @@ -0,0 +1,19 @@ +type A anydata; + +// @type T < A +type T table>; + +// @type TB < A +type TB table>; + +// @type TI < A +type TI table>; + +// @type TARR < A +type TARR table>; + +// @type TANY <> A +type TANY table>; + +// @type TERR <> A +type TERR table>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/basic-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/basic-tv.bal new file mode 100644 index 000000000000..fabd47e8f069 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/basic-tv.bal @@ -0,0 +1,17 @@ +// @type T1 < T2 +// @type T1 <> T3 +type T1 1|1.0|"foo"; +type T2 int|float|string; +type T3 int|string; + +// @type T4 = OneFoo +type T4 T3 & T1; +type OneFoo 1|"foo"; + +// @type T5 = One +// @type DoubleOne = One +// @type AnotherDoubleOne = One +type T5 1|1; +type One 1; +type DoubleOne One|One; +type AnotherDoubleOne One|1; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/bdddiff1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/bdddiff1-tv.bal new file mode 100644 index 000000000000..3012aea1fdae --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/bdddiff1-tv.bal @@ -0,0 +1,4 @@ +type INA int?[]; +type IA int[]; +// @type U = INA +type U IA|INA; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/cyclic-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/cyclic-tv.bal new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/decimal-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/decimal-tv.bal new file mode 100644 index 000000000000..257448a987f1 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/decimal-tv.bal @@ -0,0 +1,13 @@ +// @type PosZero < Decimal +// @type NegZero < Decimal +// @type OtherZero < Decimal +// @type PosZero = NegZero +// @type PosZero = OtherZero +type PosZero 0.0d; +type NegZero -0.0d; +type OtherZero 0d; + +type Decimal decimal; + +// @type IntZero <> OtherZero +type IntZero 0; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/dependently-typed-fn-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/dependently-typed-fn-tv.bal new file mode 100644 index 000000000000..b0b9d98eeae3 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/dependently-typed-fn-tv.bal @@ -0,0 +1,9 @@ +// @type F1 < F2 +type F1 function (typedesc td) returns td; + +type F2 function (typedesc td) returns anydata; + +// @type Fu1 < Fu2 +type Fu1 function (typedesc td) returns td|error; + +type Fu2 function (typedesc td) returns anydata|error; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/error1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/error1-tv.bal new file mode 100644 index 000000000000..8a188010c68f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/error1-tv.bal @@ -0,0 +1,9 @@ +// @type EL < E +// @type ER1 < E +// @type ER1 = ER2 +// @type EL <> ER1 +// @type ER2 < E +type EL error; +type ER1 error; +type ER2 error; +type E error; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-large-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-large-t.bal new file mode 100644 index 000000000000..52bcf3cdb1f8 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-large-t.bal @@ -0,0 +1,21 @@ +type IntArray int[]; +type Int5 int[5]; +type ISTArray (1|2|3)[]; + +public const int MAX_VALUE = 2147483637; +public const int MAX_VALUE_M_1 = MAX_VALUE - 1; + +// @type LargeArray < IntArray +type LargeArray int[MAX_VALUE]; + +// @type LargeArray2 < IntArray +// @type LargeArray <> LargeArray2 +type LargeArray2 int[MAX_VALUE_M_1]; + +// -@type Int5Intersection = Int5 +type Int5Intersection int[5] & !LargeArray; + +type Int10000 int[100000]; + +// -@type ISTArray < I10000A +type I10000A Int10000|(!Int10000 & IntArray); diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-t.bal new file mode 100644 index 000000000000..66421f63cf41 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-t.bal @@ -0,0 +1,68 @@ +type IntArray int[]; + +// @type Int5 < IntArray +type Int5 int[5]; + +// @type Int5 = Int5AndIntArray +// @type Int5AndIntArray < IntArray +type Int5AndIntArray Int5 & IntArray; + +// @type IntArray <> ArrayOfIntArray +type ArrayOfIntArray int[][]; + +// @type ArrayOfInt5 < ArrayOfIntArray +// @type Int5 <> ArrayOfInt5 +// @type Int5 = ArrayOfInt5[0] +// @type Int5 = ArrayOfInt5[5] +// @type Int5 = ArrayOfInt5[6] +type ArrayOfInt5 int[][5]; + +// @type Array5OfInt5 < ArrayOfInt5 +// @type Array5OfInt5 < ArrayOfIntArray +type Array5OfInt5 int[5][5]; + +type INT int; + +// @type Array5OfInt5 < Array5OfIntArray +// @type Array5OfIntArray < ArrayOfIntArray +// @type IntArray = Array5OfIntArray[0] +// @type IntArray = Array5OfIntArray[4] +type Array5OfIntArray int[5][]; + +type ROIntArray readonly & IntArray; + +// @type ROInt5 < Int5 +// @type ROInt5 < ROIntArray +type ROInt5 readonly & int[5]; + +// -@type ArrayExcept5 <> Int5; +// -@type ArrayExcept5 < IntArray; +type ArrayExcept5 IntArray & !Int5; + +const FIVE = 5; + +// @type ArrayOfInt5 = ArrayOfIntFive +type ArrayOfIntFive int[][FIVE]; + +// @type Array5OfInt5 = ArrayFiveOfIntFive +type ArrayFiveOfIntFive int[FIVE][FIVE]; + +// @type ROArrayFiveOfIntFive < ArrayFiveOfIntFive +// @type ROArrayFiveOfIntFive < Array5OfInt5 +type ROArrayFiveOfIntFive ArrayFiveOfIntFive & readonly; + +type N never; +// @type ArrayOfInt5 = TwoArraysOfInt5[0] +// @type ArrayOfInt5 = TwoArraysOfInt5[1] +// @type N = TwoArraysOfInt5[2] +type TwoArraysOfInt5 int[2][][5]; + +// @type EmptyIntArray < IntArray +type EmptyIntArray int[0]; + +type Array2OfInt5 Int5[2]; + +type Array7OfArray2OfInt5 Array2OfInt5[7]; + +// @type Array7x2x5 = Array7OfArray2OfInt5 +type Array7x2x5 int[7][2][5]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-tuple-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-tuple-t.bal new file mode 100644 index 000000000000..91cab8527bef --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array-tuple-t.bal @@ -0,0 +1,27 @@ +type Int1 int[1]; +type Int2 int[2]; + +// @type IntT = Int1 +type IntT [int]; + +// @type IntIntT = Int2 +type IntIntT [int, int]; + +// @type IntIntRT < IntIntT +// @type IntIntRT < Int2 +type IntIntRT readonly & [int, int]; + +// @type Int2R = IntIntRT +type Int2R readonly & int[2]; + +// @type Int = IntIntT[0] +type Int int; + +// @type Int = Int2Intersection[0] +// @type Int = Int2Intersection[1] +type Int2Intersection IntIntT & int[2]; + +// @type Int2Intersection = Int2AnyArrayIntersection +// @type Int = Int2AnyArrayIntersection[0] +// @type Int = Int2AnyArrayIntersection[1] +type Int2AnyArrayIntersection IntIntT & any[]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array2-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array2-t.bal new file mode 100644 index 000000000000..a66701665330 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/fixed-length-array2-t.bal @@ -0,0 +1,66 @@ +type IntArray int[]; +type ISArray (int|string)[]; +type ISTArray (1|2|3)[]; + +type Int4 int[4]; +type Int1 int[1]; +type Int14 Int4|Int1; +type NegInt14 (!Int14 & IntArray); + +// -@type I4A = IntArray +// -@type I4A < ISArray +type I4A Int4|(!Int4 & IntArray); + +type Int10000 int[100000]; + +// -@type ISTArray < I10000A +type I10000A Int10000|(!Int10000 & IntArray); + +// -@type IA = IntArray +// -@type IA < ISArray +type IA Int14|NegInt14; + +type IS int|string; +type EMPTY []; +type IS1 IS[1]; +type IS2 IS[2]; +type IS3 IS[3]; + +// @type ALL = ISArray +type ALL EMPTY|IS1|IS2|IS3|[IS, IS, IS, IS, IS...]; + +// @type ISArray < U +type U EMPTY|[IS|float, IS...]; + +// @type ISArray < V +type V EMPTY|[IS]|[IS, IS|float, IS...]; + +// @type ISArray < W +type W EMPTY|[IS]|[IS, IS|float]|[IS, IS, IS|float, IS...]; + +// @type ISArray < X +type X EMPTY|[IS]|[IS, IS|float]|[IS, IS, IS|float]|[IS, IS, IS, IS|float, IS...]; + +// @type ISArray < Y +type Y EMPTY|[IS, IS, IS, IS|float, IS...]|[IS, IS, IS|float]|[IS, IS|float]|[IS]; + +// @type ISArray < Z +type Z [IS, IS, IS, IS|float, IS...]|[IS, IS, IS|float]|[IS, IS|float]|[IS]|EMPTY; + +// @type IntArray < P +type P EMPTY|[int]|[IS, int]|[IS, IS, IS, IS...]; + +// @type IntArray < Q +type Q EMPTY|[int]|[IS, IS]|[IS, IS, IS|float, IS...]; + +// @type IntArray < R +type R EMPTY|[int]|[IS, IS]|[int, int, IS|float, IS...]; + +// @type IntArray < S +type S EMPTY|[int]|[IS, IS]|[int, int, int, IS...]; + +// @type IntArray < T +type T EMPTY|[int]|[IS, int]|[IS, IS, IS|float, IS...]; + +// @type IntArray < T1 +type T1 EMPTY|[int]|[IS, IS, string...]|[IS, IS, IS, IS...]; \ No newline at end of file diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/float-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/float-tv.bal new file mode 100644 index 000000000000..e11c0d03170c --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/float-tv.bal @@ -0,0 +1,22 @@ +// @type NegativeZero < Float +// @type Zero < Float +// @type NegativeZero = Zero + +type Zero 0.0; + +type NegativeZero -0.0; + +type Float float; + +// @type D1 <> Float +// @type D1 <> Zero +type D1 0.0d; + +// @type F1 < Float +// @type F1 = Zero +// @type F1 = NegativeZero +// @type F2 = Zero +// @type F2 = NegativeZero +type F1 Zero|NegativeZero; + +type F2 F1 & Zero; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/func-quals-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/func-quals-tv.bal new file mode 100644 index 000000000000..b1d05f965f6a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/func-quals-tv.bal @@ -0,0 +1,43 @@ +// @type IsolatedFuncTop < FuncTop +type IsolatedFuncTop isolated function; + +type FuncTop function; + +// @type FI1 < F1 +// @type FI1 < IsolatedFuncTop +// @type FI1 < FuncTop +type FI1 isolated function (); + +type F1 function (); + +type FI2 isolated function (int); + +type FI3 isolated function (int, int); + +// @type FI1 < FIX +// @type FI2 < FIX +// @type FI3 < FIX +// @type FIX < IsolatedFuncTop +// @type FIX < FuncTop +type FIX FI1|FI2|FI3; + +type F2 function (int); + +type F3 function (int, int); + +// @type FIX < FX +type FX F1|F2|F3; + +// @type F1 < FT1 +type FT1 transactional function (); + +type FT2 transactional function (int); + +type FT3 transactional function (int, int); + +// @type FT1 < FTX +// @type FT2 < FTX +// @type FT3 < FTX +// @type FX < FTX +// @type FIX < FTX +type FTX FT1|FT2|FT3; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-intersection-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-intersection-tv.bal new file mode 100644 index 000000000000..51d2c21b43b3 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-intersection-tv.bal @@ -0,0 +1,6 @@ +type F1 function(1|2|3); +type F2 function(2|3|4); + +// @type F < F1 +// @type F < F2 +type F F1&F2; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-param-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-param-tv.bal new file mode 100644 index 000000000000..92e13f181a44 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-param-tv.bal @@ -0,0 +1,7 @@ +// @type F12 < F1 +type F12 function(1|2); +type F1 function(1); + +// @type F_ret1 < F_ret12 +type F_ret12 function() returns 1|2; +type F_ret1 function() returns 1; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rec-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rec-tv.bal new file mode 100644 index 000000000000..203a184e923d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rec-tv.bal @@ -0,0 +1,7 @@ +// @type F < Fx +type F function() returns F; +type Fx function() returns function; + +// @type Gx < G +type G function(G); +type Gx function(function); diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rest-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rest-tv.bal new file mode 100644 index 000000000000..9699a0cec5a6 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-rest-tv.bal @@ -0,0 +1,8 @@ +// @type FInt < F1 +// @type FInt < F2 +// @type FInt < F3 +// @type F3 < F2 +type FInt function(int...); +type F1 function(int); +type F2 function(int, int); +type F3 function(int, int, int...); diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-tv.bal new file mode 100644 index 000000000000..a1d805b70a34 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-tv.bal @@ -0,0 +1,16 @@ +type F function; + +// @type F1 < F +// @type F1_bar < F +type F1 function(int); +type F1_bar function(int a); + +// @type F2 < F +// @type F2_bar < F +type F2 function(int) returns boolean; +type F2_bar function(int a) returns boolean; + +// @type F3 < F +// @type F3_bar < F +type F3 function(int...) returns boolean; +type F3_bar function(int... a) returns boolean; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-union-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-union-tv.bal new file mode 100644 index 000000000000..6858050e36ff --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function-union-tv.bal @@ -0,0 +1,8 @@ +type F1 function(1|2); +type F2 function(2|3); + +// @type F1 < F +// @type F2 < F +// @type F < Fx +type F F1|F2; +type Fx function(2); diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function2-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function2-tv.bal new file mode 100644 index 000000000000..1a6563cd54f9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/function2-tv.bal @@ -0,0 +1,13 @@ +type ANY any; + +// @type F1 < ANY +// @type F_INT < ANY +// @type F_INT < F1 +type F1 function(1); +type F_INT function(int); + +// @type F1_ret < ANY +// @type F_INT_ret < ANY +// @type F1_ret < F_INT_ret +type F1_ret function() returns 1; +type F_INT_ret function() returns int; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/future-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/future-tv.bal new file mode 100644 index 000000000000..0a9e4c396769 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/future-tv.bal @@ -0,0 +1,7 @@ +// @type FInt < FUTURE +// @type FByte < FInt +type FUTURE future; + +type FInt future; + +type FByte future; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/list1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/list1-tv.bal new file mode 100644 index 000000000000..d80b50d5df6e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/list1-tv.bal @@ -0,0 +1,3 @@ +// @type A <> B +type A int[2][][5]; +type B int[][]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-basic-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-basic-tv.bal new file mode 100644 index 000000000000..a17a0d5da75d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-basic-tv.bal @@ -0,0 +1,3 @@ +// @type M_INT < M_ANY +type M_ANY map; +type M_INT map; \ No newline at end of file diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-record-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-record-tv.bal new file mode 100644 index 000000000000..8b0262b000ab --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-record-tv.bal @@ -0,0 +1,32 @@ +// @type R1 < M1 +type R1 record {| int x; int...; |}; +type M1 map; + +// @type R3 = R1 +type R3 record {| int x; int ...; |}; + +// @type R4 < M2 +// @type R1 < M2 +// @type M1 < M2 +type R4 record {| int|string x; int|string ...; |}; +type M2 map; + +// @type R5 < M1 +// @type R5 < R1 +type R5 record {| int x; |}; + +// @type R6 < R1 +// @type R6 < M1 +type R6 record {| int x; int y; int ...; |}; + +// @type R7 <> R6 +type R7 record {| int x; int y; string ...; |}; + +// @type R6 < R8 +type R8 record {| int x; int y; int|string ...; |}; + +// @type R9 <> R8 +type R9 record {| int j; string k; |}; + +// @type R10 < R8 +type R10 record {| int x; int y; string j; |}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-t.bal new file mode 100644 index 000000000000..7ea9c2f212b9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping-t.bal @@ -0,0 +1,25 @@ +// @type T < S +public type T R1|map<"A">; + +public type R1 record {| + byte A; + float...; +|}; + +public type S R2|map; + +public type R2 record {| + int A; + float...; +|}; + +// @type T2504 < T2525 +public type T2504 [map<[int]>, map<1>]; + +public type T2525 (map|map)[]; + + +// @type MISI < MIS +type MISI map|map; + +type MIS map; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping1-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping1-t.bal new file mode 100644 index 000000000000..71bf0b0446ca --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping1-t.bal @@ -0,0 +1,15 @@ +// the order of type defns are intentional + +type M map; + +// @type M = N +type N map; + +// @type M1 = N +// @type M1 = M +type M1 M|record {}; + +// @type M1 = M2 +// @type M2 = M +// @type M2 = N +type M2 N|record {}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping2-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping2-t.bal new file mode 100644 index 000000000000..b12f20a25f38 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping2-t.bal @@ -0,0 +1,15 @@ +// the order of type defns are intentional + +type M map<(1|2|3)>; + +// @type M = N +type N map<(1|2|3)>; + +// @type M1 = N +// @type M1 = M +type M1 M|map<(1|2)>; + +// @type M1 = M2 +// @type M2 = M +// @type M2 = N +type M2 N|map<(1|2)>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping3-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping3-t.bal new file mode 100644 index 000000000000..81a50d3cb104 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mapping3-t.bal @@ -0,0 +1,13 @@ +type M1 map|record {}; + +// @type M1 = M2 +type M2 mapM|record {}; + +type M3 map<(1|2|3)>|map<(1|2)>; + +// @type M3 = M4 +// @type M4 < M1 +// @type M4 < M2 +// @type M3 < M1 +// @type M3 < M2 +type M4 map<(1|2|3)>|map<(1|2)>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mappingIntersect-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mappingIntersect-tv.bal new file mode 100644 index 000000000000..375bc186966c --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mappingIntersect-tv.bal @@ -0,0 +1,11 @@ +type M1 map; +type M2 map; + +// @type T1 = M1M2 +type M1M2 map; +type T1 M1 & M2; + +type M3 map; + +// @type T2 = M1M2 +type T2 M1 & M2 & M3; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mutable-record-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mutable-record-t.bal new file mode 100644 index 000000000000..c9029bf6b586 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/mutable-record-t.bal @@ -0,0 +1,82 @@ +type I record {| + int x; +|}; + +type S record {| + string x; +|}; + +type IS record {| + int|string x; +|}; + +// @type IorS < IS +type IorS I|S; + +type NN record {| + int x; + int y; +|}; + +type SS record {| + string x; + string y; +|}; + +type NS record {| + int x; + string y; +|}; + +type SN record {| + string x; + int y; +|}; + +type UU record {| + int|string x; + int|string y; +|}; + +// @type U < UU +type U NN|SS|NS|SN; + +type P record {| + I|S x; +|}; + +// @type P < Q +type Q record {| + IS x; +|}; + +type P2 record {| + I|S x; + boolean y; +|}; + +// @type P2 < Q2 +type Q2 record {| + IS x; + boolean y; +|}; + +type P3 record {| + I|S x; + boolean...; +|}; + +// @type P3 < Q3 +type Q3 record {| + IS x; + boolean...; +|}; + +type P4 record { + I|S x; +}; + +// @type P4 < Q4 +type Q4 record { + IS x; +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/not1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/not1-tv.bal new file mode 100644 index 000000000000..ae68b0827b06 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/not1-tv.bal @@ -0,0 +1,5 @@ + +type T1 int; + +// -@type T1 = T2 +type T2 int? & !(); diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-binaryops-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-binaryops-tv.bal new file mode 100644 index 000000000000..fce837339546 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-binaryops-tv.bal @@ -0,0 +1,34 @@ +type TOP object {}; +type O1 object { + int a; +}; + +type O2 object { + float b; +}; + +// @type O12 < TOP +// @type O1 < O12 +// @type O2 < O12 +type O12 O1|O2; + +type O3 object { + int a; + float b; + decimal c; +}; + +type O4 object { + int a; + float b; + string c; +}; + +type O34 O3 & O4; + +// @type OX < TOP +// @type O34 < OX +type OX object { + int a; + float b; +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-distinct-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-distinct-tv.bal new file mode 100644 index 000000000000..bae6af6458b4 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-distinct-tv.bal @@ -0,0 +1,27 @@ +// @type DistinctObject1 < ObjectTy1 +// @type DistinctObject2 < ObjectTy1 +// @type DistinctObject1 <> DistinctObject2 +// @type DistinctObject3 < DistinctObject1 + +type DistinctObject1 distinct ObjectTy1; + +type DistinctObject2 distinct ObjectTy1; + +type DistinctObject3 distinct DistinctObject1; + +type ObjectTy1 object { + int foo; + function bar() returns int; +}; + +// @type RecursiveDistinctObject1 < RecursiveObject +// @type RecursiveDistinctObject1 <> RecursiveDistinctObject2 +type RecursiveDistinctObject1 distinct object { + RecursiveDistinctObject1? oo; +}; + +type RecursiveDistinctObject2 distinct RecursiveObject; + +type RecursiveObject object { + RecursiveObject? oo; +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-qulifiers-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-qulifiers-tv.bal new file mode 100644 index 000000000000..d56694168bac --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-qulifiers-tv.bal @@ -0,0 +1,61 @@ +// @type IO1 < O1 +type IO1 isolated object { + int x; + function f() returns int; +}; + +// @type IO1 < IO2 +// @type IO2 <> O1 +type IO2 isolated object { + int x; +}; + +type O1 object { + int x; + function f() returns int; +}; + +// @type SO1 < O1 +type SO1 service object { + int x; + function f() returns int; +}; + +// @type ISO1 < O1 +// @type ISO1 < IO1 +// @type ISO1 < SO1 +type ISO1 isolated service object { + int x; + function f() returns int; +}; + +// @type CO1 < O1 +// @type CO1 <> SO1 +type CO1 client object { + int x; + function f() returns int; +}; + +// @type ICO1 < O1 +// @type ICO1 < IO1 +// @type ICO1 < CO1 +// @type ICO1 <> SO1 +// @type ICO1 <> ISO1 +type ICO1 isolated client object { + int x; + function f() returns int; +}; + +// @type I_TOP < TOP +// @type IO1 < I_TOP +type I_TOP isolated object {}; + +// @type S_TOP < TOP +// @type SO1 < S_TOP +type S_TOP service object {}; + +// @type C_TOP < TOP +// @type CO1 < C_TOP +type C_TOP client object {}; + +type TOP object {}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-rec-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-rec-tv.bal new file mode 100644 index 000000000000..f269f36c136a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-rec-tv.bal @@ -0,0 +1,18 @@ +// @type O1 = O2 +type O1 object { + O1? other; +}; + +type O2 object { + O2? other; +}; + +// @type O4 < O3 +type O3 object { + function foo(O3 other); +}; + +type O4 object { + function foo(O3 other); + function bar(O4 other); +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-resource-fn-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-resource-fn-tv.bal new file mode 100644 index 000000000000..0f7843aac4fc --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-resource-fn-tv.bal @@ -0,0 +1,32 @@ +// @type C1 <> C2 +type C1 client object { + resource function get [int](); +}; + +// @type Ci1 <> C1 +type Ci1 client object { + resource function post [int](); +}; + +type C2 client object { + resource function get [string](); +}; + +// @type C1 < C3 +type C3 client object { + resource function get [byte](); +}; + +type Cx1 client object { + resource function get foo/[int](int x); +}; + +// @type Cx1 < Cx2 +type Cx2 client object { + resource function get foo/[byte](byte x); +}; + +// @type Cx3 <> Cx2 +type Cx3 client object { + resource function get bar/[byte]/bar(byte x); +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-simple-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-simple-tv.bal new file mode 100644 index 000000000000..902997948a66 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-simple-tv.bal @@ -0,0 +1,64 @@ +// @type O1 = O2 +type O1 object { + public int a; +}; + +type O2 object { + public int a; +}; + +// @type O3 < O1 +type O3 object { + public int a; + public string b; +}; + +// @type O4 < O1 +type O4 object { + public byte a; +}; + +// @type OO1 = OO2 +type OO1 object { + public function foo(int a) returns int; +}; + +type OO2 object { + public function foo(int a) returns int; +}; + +// @type OO3 < OO1 +type OO3 object { + public function foo(int a, int... rest) returns int; +}; + +// @type OO4 < OO1 +type OO4 object { + public function foo(int a) returns int; + public int a; +}; + +// @type OO5 <> OO4 +type OO5 object { + public function (int a) returns int foo; + public int a; +}; + +// @type G3 <> O3 +// @type G3 < O1 +type G3 object { + public int a; + string b; +}; + +// @type OO4 <> GG4 +// @type GG4 < O1 +type GG4 object { + function foo(int a) returns int; + public int a; +}; + +// @type I1 < OO1 +type I1 object { + public isolated function foo(int a) returns int; +} diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-with-dependently-typed-object-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-with-dependently-typed-object-tv.bal new file mode 100644 index 000000000000..629eb7bcf547 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/object-with-dependently-typed-object-tv.bal @@ -0,0 +1,8 @@ +// @type Bar < Baz +type Bar object { + public function get(typedesc td) returns td|error; +}; + +type Baz object { + public function get(typedesc td) returns anydata|error; +}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record1-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record1-t.bal new file mode 100644 index 000000000000..2fa3c00a3606 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record1-t.bal @@ -0,0 +1,52 @@ +type M1 map; + +type R1 record {| int a; |}; + +// @type R2 < M1 +// @type R1 < R2 +type R2 record {| int a?; |}; + +// @type R2 <> R3 +type R3 record {| int? a; |}; + +// @type R4 <> R2 +type R4 record {| int a?; string b; |}; + +type R5 record {| int a; string b; |}; + +// @type R1 < R6 +// @type R2 < R6 +// @type R4 < R6 +// @type R5 < R6 +type R6 record {| int a?; string b?; |}; + +// @type R1 < R7 +// @type R2 <> R7 +// @type R4 <> R7 +// @type R5 < R7 +// @type R7 < R6 +type R7 record {| int a; string b?; |}; + +// @type R2 < R8 +type R8 record {| int|string a?; |}; + +// @type R2 < R9 +// @type R1 < R9 +type R9 record {| int|string a?; string|boolean b?; boolean c?; |}; + +// @type R1 < R10 +// @type R2 < R10 +type R10 record {| int? a?; |}; + +// @type M2 <> R1 +// @type M2 < R2 +// @type M2 <> R3 +// @type M2 <> R4 +// @type M2 <> R5 +// @type M2 < R6 +// @type M2 <> R7 +// @type M2 < R8 +// @type M2 < R9 +// @type M2 < R10 +// @type M2 < M1 +type M2 map; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record2-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record2-t.bal new file mode 100644 index 000000000000..7e17b13ff91d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record2-t.bal @@ -0,0 +1,52 @@ + +type M1 map; + +type R1 record {| int a; anydata...; |}; + +// @type M1 < R2 +// @type R1 < R2 +type R2 record {| int a?; anydata...; |}; + +// @type R2 <> R3 +type R3 record {| int? a; anydata...; |}; + +// @type R4 < R2 +type R4 record {| int a?; string b; anydata...; |}; + +type R5 record {| int a; string b; anydata...; |}; + +// @type R1 <> R6 +// @type R6 < R2 +// @type R4 < R6 +// @type R5 < R6 +type R6 record {| int a?; string b?; anydata...; |}; + +// @type R7 < R1 +// @type R7 < R2 +// @type R4 <> R7 +// @type R5 < R7 +// @type R7 < R6 +type R7 record {| int a; string b?; anydata...; |}; + +// @type R2 < R8 +type R8 record {| int|string a?; anydata...; |}; + +// @type R9 <> R2 +// @type R1 <> R9 +type R9 record {| int|string a?; string|boolean b?; boolean c?; anydata...; |}; + +// @type R1 < R10 +// @type R2 < R10 +type R10 record {| int? a?; anydata...; |}; + +// @type R1 < M2 +// @type R2 < M2 +// @type R3 < M2 +// @type R4 < M2 +// @type R5 < M2 +// @type R6 < M2 +// @type R7 < M2 +// @type R8 < M2 +// @type R9 < M2 +// @type R10 < M2 +type M2 map; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record3-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record3-t.bal new file mode 100644 index 000000000000..2be19be8b930 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/optional-field-record3-t.bal @@ -0,0 +1,173 @@ +// @type R11 < R12 +type R11 record {| int a; |}; + +type R12 record {| int a; anydata...; |}; + +// @type R11 < R21 +// @type R21 < R22 +type R21 record {| int a?; |}; + +// @type R11 < R22 +// @type R12 < R22 +type R22 record {| int a?; anydata...; |}; + +// @type R11 < R31 +// @type R31 < R32 +type R31 record {| int? a; |}; + +// @type R11 < R32 +// @type R12 < R32 +type R32 record {| int? a; anydata...; |}; + +// @type R41 < R22 +// @type R41 < R42 +type R41 record {| int a?; string b; |}; + +// @type R42 < R22 +type R42 record {| int a?; string b; anydata...; |}; + +// @type R51 < R12 +// @type R51 < R22 +// @type R51 < R32 +// @type R51 < R41 +// @type R51 < R42 +// @type R51 < R52 +type R51 record {| int a; string b; |}; + +// @type R52 < R12 +// @type R52 < R22 +// @type R52 < R32 +// @type R52 < R42 +type R52 record {| int a; string b; anydata...; |}; + +// @type R11 < R61 +// @type R21 < R61 +// @type R61 < R22 +// @type R41 < R61 +// @type R51 < R61 +// @type R61 < R62 +type R61 record {| int a?; string b?; |}; + +// @type R11 < R62 +// @type R21 < R62 +// @type R41 < R62 +// @type R42 < R62 +// @type R51 < R62 +// @type R52 < R62 +// @type R62 < R22 +type R62 record {| int a?; string b?; anydata...; |}; + +// @type R11 < R71 +// @type R71 < R12 +// @type R71 < R22 +// @type R71 < R32 +// @type R71 < R61 +// @type R71 < R62 +// @type R51 < R71 +// @type R71 < R72 +type R71 record {| int a; string b?; |}; + +// @type R11 < R72 +// @type R72 < R12 +// @type R72 < R22 +// @type R72 < R32 +// @type R51 < R72 +// @type R52 < R72 +// @type R72 < R62 +type R72 record {| int a; string b?; anydata...; |}; + +// @type R11 < R81 +// @type R21 < R81 +// @type R81 < R82 +type R81 record {| int|string a?; |}; + +// @type R11 < R82 +// @type R12 < R82 +// @type R21 < R82 +// @type R22 < R82 +// @type R42 < R82 +// @type R41 < R82 +// @type R51 < R82 +// @type R52 < R82 +// @type R61 < R82 +// @type R62 < R82 +// @type R71 < R82 +// @type R72 < R82 +type R82 record {| int|string a?; anydata...; |}; + +// @type R11 < R91 +// @type R21 < R91 +// @type R41 < R91 +// @type R51 < R91 +// @type R61 < R91 +// @type R71 < R91 +// @type R81 < R91 +// @type R91 < R82 +// @type R91 < R92 +type R91 record {| int|string a?; string|boolean b?; boolean c?; |}; + +// @type R11 < R92 +// @type R21 < R92 +// @type R41 < R92 +// @type R51 < R92 +// @type R61 < R92 +// @type R71 < R92 +// @type R81 < R92 +// @type R92 < R82 +type R92 record {| int|string a?; string|boolean b?; boolean c?; anydata...; |}; + +// @type R11 < R101 +// @type R21 < R101 +// @type R31 < R101 +// @type R101 < R102 +type R101 record {| int? a?; |}; + +// @type R11 < R102 +// @type R12 < R102 +// @type R21 < R102 +// @type R22 < R102 +// @type R31 < R102 +// @type R41 < R102 +// @type R42 < R102 +// @type R51 < R102 +// @type R52 < R102 +// @type R61 < R102 +// @type R62 < R102 +// @type R71 < R102 +// @type R72 < R102 +type R102 record {| int? a?; anydata...; |}; + +// @type M1 < R21 +// @type M1 < R22 +// @type M1 < R61 +// @type M1 < R62 +// @type M1 < R81 +// @type M1 < R82 +// @type M1 < R91 +// @type M1 < R92 +// @type M1 < R101 +// @type M1 < R102 +// @type M1 < M2 +type M1 map; + +// @type R11 < M2 +// @type R12 < M2 +// @type R21 < M2 +// @type R22 < M2 +// @type R31 < M2 +// @type R32 < M2 +// @type R41 < M2 +// @type R42 < M2 +// @type R51 < M2 +// @type R52 < M2 +// @type R61 < M2 +// @type R62 < M2 +// @type R71 < M2 +// @type R72 < M2 +// @type R81 < M2 +// @type R82 < M2 +// @type R91 < M2 +// @type R92 < M2 +// @type R101 < M2 +// @type R102 < M2 +type M2 map; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj1-tv.bal new file mode 100644 index 000000000000..9481bf4bb97b --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj1-tv.bal @@ -0,0 +1,41 @@ +type B boolean; + +// @type TF = B +type TF true|false; + +// @type T < TF +type T true; + +// @type F < B +type F false; + +// @type INTEGER <> B +type INTEGER int; + +type S string; +type I int; +type N 2; +const ONE = 1; + +// @type BL[1] = B +// @type BL[2] = B +// @type BL[I] = B +// @type BL[N] = B +// @type BL[ONE] = B +type BL boolean[]; + +// @type M[S] = B +type M map; + +type f1 "f1"; +type f2 "f2"; +const FOO = "f2"; + +// @type R[f1] = INTEGER +// @type R[f2] = B +// @type R[FOO] = B +type R record {| + int f1; + boolean f2; +|}; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj10-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj10-tv.bal new file mode 100644 index 000000000000..b23b7c7614b1 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj10-tv.bal @@ -0,0 +1,22 @@ +type NEVER never; +type INT int; +type FLOAT float; + +type FirstFive 0|1|2|3|4|5; +type FFFloat FirstFive|float; + +type T1 [int...]; +type T2 [int, int, int...]; + +// @type T3[0] = NEVER +// @type T3[1] = NEVER +// @type T3[1] = NEVER +type T3 T1 & !T2; + +type T4 [FirstFive|float...]; +type T5 [int, int, FirstFive...]; + +// @type T6[0] = FFFloat +// @type T6[1] = FFFloat +// @type T6[3] = FFFloat +type T6 T4 & !T5; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj2-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj2-tv.bal new file mode 100644 index 000000000000..0dd6026f4e1a --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj2-tv.bal @@ -0,0 +1,6 @@ +type I int; + +// -@type A[I] = I +// -@type A[0] = I +// -@type A[1] = I +type A [int?, int?] & ![(), ()] & ![int, ()] & ![(), int]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj3-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj3-tv.bal new file mode 100644 index 000000000000..ce6d648ef2c7 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj3-tv.bal @@ -0,0 +1,13 @@ +type IB int|boolean; +type SB string|boolean; +type C string:Char; +type NonC string & !C; + +// @type R[C] = IB +// -@type R[NonC] = SB +type R record {| + int a; + int b; + string fieldName; + boolean...; +|}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj4-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj4-tv.bal new file mode 100644 index 000000000000..7fd417cabbfe --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj4-tv.bal @@ -0,0 +1,11 @@ +const ZERO = 0; +const ONE = 1; +type Index ZERO|ONE; +type Int int; +type Str string; +type IntStr Int|Str; + +// @type T[ZERO] = Int +// @type T[ONE] = Str +// @type T[Index] = IntStr +type T [int, string]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj5-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj5-tv.bal new file mode 100644 index 000000000000..3e0de9d8ed3f --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj5-tv.bal @@ -0,0 +1,14 @@ +const THREE = 3; +type F float; +type Int int; +type ISF int|string|float; +type IF int|float; +type SF string|float; +type T02 0|2; +type T12 1|2; + +// @type T[THREE] = F +// @type T[Int] = ISF +// @type T[T02] = IF +// @type T[T12] = SF +type T [int, string, float...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj6-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj6-tv.bal new file mode 100644 index 000000000000..7db777560fb1 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj6-tv.bal @@ -0,0 +1,19 @@ +const THREE = 3; +const FOUR = 4; +type F float; +type Int int; +type ISF int|string|float; +type IF int|float; +type SF string|float; +type T02 0|2; +type T12 1|2; +type NEVER never; + +type T1 [int, string, float...]; + +// @type T[THREE] = F +// @type T[FOUR] = NEVER +// @type T[Int] = ISF +// @type T[T02] = IF +// @type T[T12] = SF +type T T1 & any[4]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj7-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj7-tv.bal new file mode 100644 index 000000000000..00c78786b66d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj7-tv.bal @@ -0,0 +1,25 @@ +type NEVER never; +type BOOL boolean; +type BOOLOPT boolean?; +type INT int; +type NIL (); +type INTOPT int?; +type STROPT string?; +type INTSTROPT int|string?; +type INT_FLOAT_BOOL_OPT int|float|boolean?; +type STR_FLOAT_BOOL_OPT string|float|boolean?; +type INT_STR_FLOAT_BOOL_OPT int|string|float|boolean?; + +type C01 0|1; +type C02 0|2; +type C12 1|2; + +type T1 [int?, string?, float|boolean...]; +type T2 [int, (string|float)...]; +// @type T3[0] = INTOPT +// @type T3[1] = STROPT +// @type T3[C01] = INTSTROPT +// @type T3[C02] = INT_FLOAT_BOOL_OPT +// @type T3[C12] = STR_FLOAT_BOOL_OPT +// @type T3[INT] = INT_STR_FLOAT_BOOL_OPT +type T3 T1 & !T2; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj8-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj8-tv.bal new file mode 100644 index 000000000000..080363062f2d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj8-tv.bal @@ -0,0 +1,20 @@ +type INT int; +type STRING string; +type STROPT string?; +type NIL (); + +type C01 0|1; +type C02 0|2; +type C12 1|2; + +type T1 [int?, string...]; +type T2 [int, (string|float)...]; +// @type T3[0] = NIL +// @type T3[1] = STRING +// @type T3[2] = STRING +// @type T3[100] = STRING +// @type T3[C01] = STROPT +// @type T3[C02] = STROPT +// @type T3[C12] = STRING +// @type T3[INT] = STROPT +type T3 T1 & !T2; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj9-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj9-tv.bal new file mode 100644 index 000000000000..b84619878962 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/proj9-tv.bal @@ -0,0 +1,30 @@ +type INT int; +type INTFLOAT int|float; +type NEVER never; + +type C01 0|1; +type C02 0|2; +type C12 1|2; + +type NOTC01 !C01 & int; + +type T1 int[100000]; +type T2 [C01, C01, C01, (int|float)...]; + + +// @type T3[0] = NOTC01 +// @type T3[100] = INT +// @type T3[1000] = INT +// @type T3[10000] = INT +// @type T3[99999] = INT +// @type T3[100000] = NEVER +type T3 T1 & !T2; + +// @type T4[0] = C01 +// @type T4[1] = C01 +// @type T4[2] = C01 +// @type T4[3] = INTFLOAT +// @type T4[100] = INTFLOAT +// @type T4[1000] = INTFLOAT +// @type T4[100000] = INTFLOAT +type T4 T2 & !T1; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-proj-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-proj-tv.bal new file mode 100644 index 000000000000..fc65b365f1eb --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-proj-tv.bal @@ -0,0 +1,45 @@ +type A record {| + int x; + string y; +|}; + +// @type B[XorY] = IorS +// @type B[other] = NEVER +type B record {| + string x; + int y; +|}; + +// @type C[other] = BOOLEAN +// @type C[XorY] = IorS +// @type C[XorYorOther] = IorSOrB +type C record {| + string x; + int y; + float z; + boolean...; +|}; + +type IorS int|string; +type IorSOrB IorS|boolean; +type IorSorF IorS|float; + +const x = "x"; +const z = "z"; +const other = "other"; +type XorY "x"|"y"; +type NEVER never; +type BOOLEAN boolean; +type FLOAT float; + +type XorYorOther XorY|other; + +// @type AorB[x] = IorS +// @type AorB[XorY] = IorS +type AorB A|B; + +// @type AorBorC[x] = IorS +// @type AorBorC[z] = FLOAT +// @type AorBorC[other] = BOOLEAN +// @type AorBorC[XorYorOther] = IorSOrB +type AorBorC AorB|C; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-t.bal new file mode 100644 index 000000000000..b6ad2c517c54 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/record-t.bal @@ -0,0 +1,16 @@ +// the order of type defns are intentional + +type T1 record {| + 65 X; +|}; + +type T2 record {| + int:Signed8 X; +|}; + +// @type T1 < T3 +type T3 T4|T2; + +type T4 record {| + int:Signed8 a; +|}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/recursive-record-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/recursive-record-t.bal new file mode 100644 index 000000000000..1a6b0375ae40 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/recursive-record-t.bal @@ -0,0 +1,9 @@ +type Bdd Node|boolean; + +// @type Node < Bdd +type Node readonly & record {| + int atom; + Bdd left; + Bdd middle; + Bdd right; +|}; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-recursive-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-recursive-tv.bal new file mode 100644 index 000000000000..5b96f55fc9ca --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-recursive-tv.bal @@ -0,0 +1,8 @@ +// @type SR < S +type SR stream; + +type S stream; + +// @type S1 < SR +// @type S1 < S +type S1 stream<()>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype-tv.bal new file mode 100644 index 000000000000..20237c2bc796 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype-tv.bal @@ -0,0 +1,10 @@ +// @type I < U1 +// @type I < U2 +// @type S < U1 +// @type S < U2 +// @type U1 < U2 + +type I stream; +type S stream; +type U1 I|S; +type U2 stream; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype2-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype2-tv.bal new file mode 100644 index 000000000000..32ccf067c9fe --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/stream-subtype2-tv.bal @@ -0,0 +1,19 @@ +// @type I < J +// @type I < J1 +// @type I < J2 +type I stream; + +// @type S < J +// @type S < J1 +// @type S < J2 +type S stream; + +type T int|string|(); +// @type J = J1 +type J stream; +type J1 stream; + +// @type J < J2 +// @type J1 < J2 +type J2 stream; +type J3 stream; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/string-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/string-tv.bal new file mode 100644 index 000000000000..db4f84e356ac --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/string-tv.bal @@ -0,0 +1,16 @@ +// @type U1 < String +// @type U1 < Char +type U1 "අ"; +// @type U2 < String +// @type U2 < Char +type U2 "🛧"; +// @type C1 < String +// @type C1 < Char +type C1 "a"; + +// @type S1 < String +// @type S1 <> Char +type S1 "abc"; + +type String string; +type Char string:Char; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-readonly-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-readonly-t.bal new file mode 100644 index 000000000000..ebebd154b787 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-readonly-t.bal @@ -0,0 +1,25 @@ +type R record {| + int id; + int f; +|}; + +type R1 record {| + int id; + int f; + float d; +|}; + +type READ readonly; + +// @type W < T +// @type W < READ +// @type T < READ +// @type W < Y1 +// @type Y1 <> T +// -@type Z < Y1 +// -@type Z <> T +type T table & readonly | table & readonly; +type W table & readonly; +type Y1 table; +type Y table; +type Z table & !readonly; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-t.bal new file mode 100644 index 000000000000..9c52d92c3703 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table-t.bal @@ -0,0 +1,13 @@ +type X1 record {| + int id; + string f; +|}; + +type X2 record {| + int id; + string:Char f; +|}; + +// @type T2 < T1 +type T1 table; +type T2 table; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table2-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table2-t.bal new file mode 100644 index 000000000000..f7e40035b911 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table2-t.bal @@ -0,0 +1,31 @@ +type R1 record {| + int id; + string f; +|}; + +type R2 record {| + int id; + string f; + float d; +|}; + +type R3 record {| + int id; + string:Char f; +|}; + +type READ readonly; + +type T1 table; +type T2 table; +type T3 table; + +// @type TI < T1 +// @type TI = T3 +// @type T1 < TU +// @type T2 < TU +type TI T1 & T3; +type TU T1|T2; + +// -@type T1 <> TC +type TC !T1; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table3-t.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table3-t.bal new file mode 100644 index 000000000000..0c639556f062 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/table3-t.bal @@ -0,0 +1,27 @@ +type X1 record {| + int x; +|}; + +type X2 record {| + string x; +|}; + +type T1 table; +type T2 table; +type T3 T1|T2; +// @type T3 < T4 +type T4 table; + + +type X3 map; +type X4 map; + +type T5 table; +type T6 table; +type T7 T5|T6; +// @type T7 < T8 +type T8 table; + +// @type T8 < T9 +// @type T7 < T9 +type T9 table>; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test_test.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test_test.bal new file mode 100644 index 000000000000..009d8ac1322c --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/test_test.bal @@ -0,0 +1,17 @@ +type I int; +type F float; +type S string; + +// @type I <> F + +type K decimal; + +type M map; + +// @type M[S] = I + +// @type L[I] = I +type L int[]; + +// @type SL[I] = S +type SL string[]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple1-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple1-tv.bal new file mode 100644 index 000000000000..cfac1e3640a7 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple1-tv.bal @@ -0,0 +1,16 @@ +type R record {| + int f1; + boolean f2; +|}; + +// @type A1 = T1 +type A1 int[]; +type T1 [int...]; + +// @type A2 = T2 +type A2 R[]; +type T2 [R...]; + +// @type A3 = T3 +type A3 T2[]; +type T3 [T2...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple2-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple2-tv.bal new file mode 100644 index 000000000000..d0f64d6d92ad --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple2-tv.bal @@ -0,0 +1,14 @@ +type IS16 int:Signed16; +type I int; +type I2 int?; + +// @type T1 <> T2 +type T1 [int, string]; +type T2 [string, int]; + +// @type T4 < T3 +// @type T3[0] = I2 +// @type T3[1] = I +// @type T4[1] = IS16 +type T3 [int?, int, any]; +type T4 [int, int:Signed16, int]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple3-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple3-tv.bal new file mode 100644 index 000000000000..9a12b1aed2cc --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple3-tv.bal @@ -0,0 +1,20 @@ +type R record {| + int f1; + boolean f2; +|}; + +type R1 record {| + int f1; + boolean f2; +|}; + +type R2 record {| + int:Signed16 f1; + boolean f2; +|}; + +// @type T1 = T +// @type T2 < T +type T [R...]; +type T1 [R1...]; +type T2 [R2...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple4-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple4-tv.bal new file mode 100644 index 000000000000..537b8c836d1d --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/tuple4-tv.bal @@ -0,0 +1,7 @@ +// @type T2 < T1 +type T1 [int...]; +type T2 [int,int...]; + +// @type T3 < T1 +type T4 [int?,int?...]; +type T3 [int, int, int, int...]; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/typedesc-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/typedesc-tv.bal new file mode 100644 index 000000000000..146649ab6d7e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/typedesc-tv.bal @@ -0,0 +1,10 @@ +// @type I < U1 +// @type I < U2 +// @type S < U1 +// @type S < U2 +// @type U1 < U2 + +type I typedesc; +type S typedesc; +type U1 I|S; +type U2 typedesc; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-ro-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-ro-tv.bal new file mode 100644 index 000000000000..6716b76bfc66 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-ro-tv.bal @@ -0,0 +1,38 @@ +type X xml; +type RX readonly & X; + +type N xml; +type T xml:Text; +type E readonly & xml:Element; +type P readonly & xml:ProcessingInstruction; +type C readonly & xml:Comment; +type XE xml; +type XP xml

; +type XC xml; + +type ReadOnlyFlat T|E|P|C; + +// -@type NonEmptyRoSingletons < ReadOnlyFlat +// -@type NonEmptyRoSingletons <> T +// -@type NonEmptyRoSingletons <> N +// -@type E < NonEmptyRoSingletons +// -@type P < NonEmptyRoSingletons +// -@type C < NonEmptyRoSingletons +type NonEmptyRoSingletons ReadOnlyFlat & !N; + +// -@type NonEmptyRoSingletons < UX +type UX XE|XP|XC|T; + +// -@type XNonEmptyRoSingletons = RX +// -@type XNonEmptyRoSingletons < X +type XNonEmptyRoSingletons xml; + +// @type XUX = RX +type XUX xml; + +type NEVER never; +type RWX X & !readonly; + +// -@type RX_UNION_RO = X +type RX_UNION_RO RX | RWX; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-rw-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-rw-tv.bal new file mode 100644 index 000000000000..9c9e293ca8c9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-complex-rw-tv.bal @@ -0,0 +1,30 @@ +type X xml; + +type N xml; +type T xml:Text; +type E xml:Element; +type P xml:ProcessingInstruction; +type C xml:Comment; +type XE xml; +type XP xml

; +type XC xml; + +type S T|E|P|C; + +// -@type NonEmptyS < S +// -@type NonEmptyS <> T +// -@type NonEmptyS <> N +// -@type E < NonEmptyS +// -@type P < NonEmptyS +// -@type C < NonEmptyS +type NonEmptyS S & !N; + +// -@type NonEmptyS < UX +type UX XE|XP|XC|T; + +// -@type XNonEmptyS = X +type XNonEmptyS xml; + +// @type XUX = X +type XUX xml; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-never-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-never-tv.bal new file mode 100644 index 000000000000..7a4059117df9 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-never-tv.bal @@ -0,0 +1,31 @@ +type N xml; + +// @type N = NS +type NS xml; + +// @type N <> E +type E xml:Element; + +// @type N < T +type T xml:Text; + +// @type N <> C +type C xml:Comment; + +// @type N <> P +type P xml:ProcessingInstruction; + +// @type N < ES +type ES xml; + +// @type N < CS +type CS xml; + +// @type N < TS +type TS xml; + +// @type N < PS +type PS xml; + +// @type ENS = ES +type ENS xml; diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-readonly-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-readonly-tv.bal new file mode 100644 index 000000000000..1256d31d26e7 --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-readonly-tv.bal @@ -0,0 +1,33 @@ +type R readonly; +type X xml; + +// @type N < R +type N xml; + +// @type T < R +// @type N < T +type T xml:Text; + +// @type N = RN +type RN readonly & N; + +// @type RO_E < R +type RO_E xml:Element & readonly; + +// @type RO_C < R +type RO_C xml:Comment & readonly; + +// @type RO_P < R +type RO_P xml:ProcessingInstruction & readonly; + +// @type RX < R +// @type RX < X +type RX readonly & X; + +// @type RX = RO_XML +type RO_XML xml; + +// @type RO_XML_INTERSECTION = RO_XML +// @type RO_XML_INTERSECTION = RX +type RO_XML_INTERSECTION RO_XML & readonly; + diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-sequence-tv.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-sequence-tv.bal new file mode 100644 index 000000000000..e37193290a6e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-sequence-tv.bal @@ -0,0 +1,40 @@ + +type X xml; + +// @type X = Y +type Y xml; + +// @type X = P +// @type Y = P +type P xml; + +// @type X = Q +type Q xml

; + +// @type U < X +type U xml; + +// @type V < X +// @type U < V +type V xml; + +// @type N < X +// @type N < V +// @type N < U +type N xml; + +// @type N <> E +// @type E < V +// @type E < X +type E xml:Element; + +// @type XE = X +type XE xml; + +// @type XEU = X +type XEU xml|E; + +type T xml:Text; + +// @type T = XT +type XT xml; \ No newline at end of file diff --git a/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-te.bal b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-te.bal new file mode 100644 index 000000000000..c923d65298fd --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/test-src/type-rel/xml-te.bal @@ -0,0 +1,7 @@ +type X xml; +type RX readonly & X; +type NEVER never; + +type RWX X & !readonly; + +type RX_MINUS_RO RX & RWX; // @error diff --git a/tests/jballerina-semtype-port-test/src/test/resources/testng.xml b/tests/jballerina-semtype-port-test/src/test/resources/testng.xml new file mode 100644 index 000000000000..e1138781de8e --- /dev/null +++ b/tests/jballerina-semtype-port-test/src/test/resources/testng.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + + + diff --git a/tests/jballerina-semtype-test/src/test/java/io/ballerina/test/SemTypeTest.java b/tests/jballerina-semtype-test/src/test/java/io/ballerina/test/SemTypeTest.java index 80a1c769613e..d494a411fcf3 100644 --- a/tests/jballerina-semtype-test/src/test/java/io/ballerina/test/SemTypeTest.java +++ b/tests/jballerina-semtype-test/src/test/java/io/ballerina/test/SemTypeTest.java @@ -55,7 +55,7 @@ */ public class SemTypeTest { - private final Types types = Types.getInstance(new CompilerContext()); + private Types types; @DataProvider(name = "filePathProvider") public Object[] filePathProvider() { @@ -63,7 +63,7 @@ public Object[] filePathProvider() { List files = new ArrayList<>(); for (File file : Objects.requireNonNull(dir.listFiles())) { String fileName = file.getName(); - if (fileName.endsWith(FAILING_FILE) || fileName.endsWith(DISABLED_FILE)) { + if (!fileName.endsWith(".bal") || fileName.endsWith(FAILING_FILE) || fileName.endsWith(DISABLED_FILE)) { continue; } files.add(file.getAbsolutePath()); @@ -83,6 +83,9 @@ public void testSubTypeRelationship(String filePath) throws IOException { private Set actualSubTypeRelations(String filePath) { CompileResult compileResult = BCompileUtil.compile(filePath); BLangPackage bLangPackage = (BLangPackage) compileResult.getAST(); + // Need to use the same semtypeEnv used in the compiler as we keep cache for some BTypes. + types = new Types(new CompilerContext(), bLangPackage.semtypeEnv); + List bTypeDefinitionSymbols = new ArrayList<>(); for (Scope.ScopeEntry value : bLangPackage.symbol.scope.entries.values()) { BSymbol bSymbol = value.symbol; diff --git a/tests/jballerina-semtype-test/src/test/resources/test-src/proj1-f.bal b/tests/jballerina-semtype-test/src/test/resources/test-src/proj1-f.bal deleted file mode 100644 index 980df3145686..000000000000 --- a/tests/jballerina-semtype-test/src/test/resources/test-src/proj1-f.bal +++ /dev/null @@ -1,5 +0,0 @@ -// B<:TF -type B boolean; -type TF true|false; -// B should be a subtype of TF -// BUG #34711 diff --git a/tests/jballerina-semtype-test/src/test/resources/test-src/proj1.bal b/tests/jballerina-semtype-test/src/test/resources/test-src/proj1.bal index 9f50bee77dec..48de5616ac03 100644 --- a/tests/jballerina-semtype-test/src/test/resources/test-src/proj1.bal +++ b/tests/jballerina-semtype-test/src/test/resources/test-src/proj1.bal @@ -1,3 +1,4 @@ // TF<:B +// B<:TF type B boolean; type TF true|false; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/TypeReference.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/TypeReference.java index d5b51e67fa30..688d474423d0 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/TypeReference.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/runtime/api/tests/TypeReference.java @@ -18,6 +18,7 @@ package org.ballerinalang.nativeimpl.jvm.runtime.api.tests; import io.ballerina.runtime.api.creators.ErrorCreator; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.ObjectType; import io.ballerina.runtime.api.types.Parameter; import io.ballerina.runtime.api.types.ReferenceType; @@ -39,7 +40,6 @@ import io.ballerina.runtime.internal.types.BErrorType; import io.ballerina.runtime.internal.types.BFunctionType; import io.ballerina.runtime.internal.types.BIntersectionType; -import io.ballerina.runtime.internal.types.BMapType; import io.ballerina.runtime.internal.types.BParameterizedType; import io.ballerina.runtime.internal.types.BRecordType; import io.ballerina.runtime.internal.types.BStreamType; @@ -112,7 +112,7 @@ public static Boolean validateIntersectionType(BTypedesc typedesc) { } public static Boolean validateMapType(BTypedesc typedesc) { - BMapType mapType = (BMapType) TypeUtils.getImpliedType(typedesc.getDescribingType()); + MapType mapType = (MapType) TypeUtils.getImpliedType(typedesc.getDescribingType()); if (mapType.getConstrainedType().getTag() != TypeTags.TYPE_REFERENCED_TYPE_TAG) { throw ErrorCreator.createError(StringUtils.fromString("map type API provided a non type reference " + diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/tests/VariableReturnType.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/tests/VariableReturnType.java index 891e1beb46a4..4e7ed88a701d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/tests/VariableReturnType.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/nativeimpl/jvm/tests/VariableReturnType.java @@ -198,10 +198,10 @@ public static MapValue getRecord(BTypedesc td) { BRecordType recType = (BRecordType) td.getDescribingType(); MapValueImpl person = new MapValueImpl<>(recType); - if (recType.getName().equals("Person")) { + if (recType.getName().contains("Person")) { person.put(NAME, JOHN_DOE); person.put(AGE, 20); - } else if (recType.getName().equals("Employee")) { + } else if (recType.getName().contains("Employee")) { person.put(NAME, JANE_DOE); person.put(AGE, 25); person.put(DESIGNATION, SOFTWARE_ENGINEER); @@ -226,7 +226,7 @@ public static Object getVariedUnion(long x, BTypedesc td1, BTypedesc td2) { } MapValueImpl rec = new MapValueImpl<>(type2); - if (type2.getName().equals("Person")) { + if (type2.getName().contains("Person")) { rec.put(NAME, JOHN_DOE); rec.put(AGE, 20); } else { @@ -279,10 +279,10 @@ private static Object getValue(Type type) { BRecordType recType = (BRecordType) type; MapValueImpl person = new MapValueImpl<>(recType); - if (recType.getName().equals("Person")) { + if (recType.getName().contains("Person")) { person.put(NAME, JOHN_DOE); person.put(AGE, 20); - } else if (recType.getName().equals("Employee")) { + } else if (recType.getName().contains("Employee")) { person.put(NAME, JANE_DOE); person.put(AGE, 25); person.put(DESIGNATION, SOFTWARE_ENGINEER); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/start/StartActionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/start/StartActionTest.java index 08e3b060a65e..3a403d6e41ee 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/start/StartActionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/action/start/StartActionTest.java @@ -59,7 +59,6 @@ public void testStartActionNegative() { BAssertUtil.validateError(result, indx++, "'wait' cannot be used with actions", 72, 18); BAssertUtil.validateError(result, indx++, "action invocation as an expression not allowed here", 72, 28); BAssertUtil.validateError(result, indx++, "action invocation as an expression not allowed here", 76, 25); - BAssertUtil.validateError(result, indx++, "incompatible types: expected 'other', found 'int'", 90, 13); BAssertUtil.validateError(result, indx++, "incompatible types: '(int[]|error)' is not an iterable collection" , 90, 22); BAssertUtil.validateError(result, indx++, "'wait' cannot be used with actions", 90, 27); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/bala/functions/DependentlyTypedFunctionsBalaTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/bala/functions/DependentlyTypedFunctionsBalaTest.java index 66ce02a19730..9c7d8b31021a 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/bala/functions/DependentlyTypedFunctionsBalaTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/bala/functions/DependentlyTypedFunctionsBalaTest.java @@ -50,7 +50,7 @@ public void testRuntimeCastError() { @Test(expectedExceptions = BLangTestException.class, expectedExceptionsMessageRegExp = ".*error: \\{ballerina}TypeCastError \\{\"message\":\"incompatible types:" + - " 'Person' cannot be cast to 'int'\"}.*") + " 'PersonDTBT' cannot be cast to 'int'\"}.*") public void testCastingForInvalidValues() { BRunUtil.invoke(result, "testCastingForInvalidValues"); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java index acbed8f5c684..0ab237cfc05f 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/EqualAndNotEqualOperationsTest.java @@ -127,7 +127,8 @@ public Object[] getMapTestFunctions() { return new String[]{ "checkMapEqualityPositive", "checkMapEqualityNegative", "checkComplexMapEqualityPositive", "checkComplexMapEqualityNegative", "checkUnionConstrainedMapsPositive", - "checkUnionConstrainedMapsNegative", "testEmptyMapAndRecordEquality" + "checkUnionConstrainedMapsNegative", "testEmptyMapAndRecordEquality", + "checkEqualityOfMapsOfIncompatibleConstraintTypes" }; } @@ -200,6 +201,18 @@ public void selfAndCyclicReferencingFunctions(String testFunctionName) { BRunUtil.invoke(result, testFunctionName); } + @Test(dataProvider = "getReadonlyEqualityFunctions") + public void testReadonlyEquality(String testFunctionName) { + BRunUtil.invoke(result, testFunctionName); + } + + @DataProvider(name = "getReadonlyEqualityFunctions") + public Object[] getReadonlyEqualityFunctions() { + return new String[]{ + "readonlyMapEquality", "readonlyListEquality" + }; + } + @Test(description = "Test equal and not equal with errors") public void testEqualAndNotEqualNegativeCases() { int i = 0; @@ -207,50 +220,43 @@ public void testEqualAndNotEqualNegativeCases() { validateError(resultNegative, i++, "operator '!=' not defined for 'int' and 'string'", 20, 24); validateError(resultNegative, i++, "operator '==' not defined for 'int[2]' and 'string[2]'", 26, 21); validateError(resultNegative, i++, "operator '!=' not defined for 'int[2]' and 'string[2]'", 26, 33); - validateError(resultNegative, i++, "operator '==' not defined for 'map' and 'map'", 38, 21); - validateError(resultNegative, i++, "operator '!=' not defined for 'map' and 'map'", 38, 33); - validateError(resultNegative, i++, "operator '==' not defined for 'map<(string|int)>' and 'map'", - 42, 21); - validateError(resultNegative, i++, "operator '!=' not defined for 'map<(string|int)>' and 'map'", - 42, 33); validateError(resultNegative, i++, "operator '==' not defined for '[string,int]' and '[boolean,float]'", - 50, 21); + 38, 21); validateError(resultNegative, i++, "operator '!=' not defined for '[string,int]' and '[boolean,float]'", - 50, 33); + 38, 33); validateError(resultNegative, i++, "operator '==' not defined for '[(float|int),int]' and '[boolean,int]'", - 54, 21); + 42, 21); validateError(resultNegative, i++, "operator '!=' not defined for '[(float|int),int]' and '[boolean,int]'", - 54, 33); - validateError(resultNegative, i++, "operator '==' not defined for 'Employee' and 'Person'", 62, 17); - validateError(resultNegative, i++, "operator '!=' not defined for 'Employee' and 'Person'", 62, 29); - validateError(resultNegative, i++, "operator '==' not defined for 'EmployeeWithOptionalId' and " + - "'PersonWithOptionalId'", 66, 17); - validateError(resultNegative, i++, "operator '!=' not defined for 'EmployeeWithOptionalId' and " + - "'PersonWithOptionalId'", 66, 31); - validateError(resultNegative, i++, "operator '==' not defined for 'map' and 'ClosedDept'", 75, 23); - validateError(resultNegative, i++, "operator '!=' not defined for 'ClosedDept' and 'map'", 75, 35); - validateError(resultNegative, i++, "operator '==' not defined for 'int[]' and '[float,float]'", 82, 23); - validateError(resultNegative, i++, "operator '!=' not defined for 'int[]' and '[float,float]'", 82, 35); - validateError(resultNegative, i++, "operator '==' not defined for 'int[]' and '[int,float]'", 85, 23); - validateError(resultNegative, i++, "operator '!=' not defined for '[int,float]' and 'int[]'", 85, 35); - validateError(resultNegative, i++, "operator '==' not defined for 'Employee' and '()'", 138, 9); - validateError(resultNegative, i++, "operator '==' not defined for 'Foo' and '()'", 144, 9); + 42, 33); + validateError(resultNegative, i++, "operator '==' not defined for 'Employee' and 'Person'", 50, 17); + validateError(resultNegative, i++, "operator '!=' not defined for 'Employee' and 'Person'", 50, 29); + validateError(resultNegative, i++, "operator '==' not defined for 'map' and 'ClosedDept'", 59, 23); + validateError(resultNegative, i++, "operator '!=' not defined for 'ClosedDept' and 'map'", 59, 35); + validateError(resultNegative, i++, "operator '==' not defined for 'int[]' and '[float,float]'", 66, 23); + validateError(resultNegative, i++, "operator '!=' not defined for 'int[]' and '[float,float]'", 66, 35); + validateError(resultNegative, i++, "operator '==' not defined for 'int[]' and '[int,float]'", 69, 23); + validateError(resultNegative, i++, "operator '!=' not defined for '[int,float]' and 'int[]'", 69, 35); + validateError(resultNegative, i++, "operator '==' not defined for 'Employee' and '()'", 117, 9); + validateError(resultNegative, i++, "operator '==' not defined for 'Foo' and '()'", 123, 9); validateError(resultNegative, i++, "operator '==' not defined for 'function () returns (string)' and '()'", - 150, 9); - validateError(resultNegative, i++, "operator '!=' not defined for 'readonly' and 'map'", - 168, 12); - validateError(resultNegative, i++, "operator '==' not defined for '[int,map]' and '[int,float]'", 179, + 129, 9); + validateError(resultNegative, i++, "operator '==' not defined for '[int,map]' and '[int,float]'", 142, 23); - validateError(resultNegative, i++, "operator '!=' not defined for '[int,float]' and '[int,map]'", 179, + validateError(resultNegative, i++, "operator '!=' not defined for '[int,float]' and '[int,map]'", 142, 35); - validateError(resultNegative, i++, "operator '==' not defined for 'MyObject' and '()'", 182, + validateError(resultNegative, i++, "operator '==' not defined for 'MyObject' and '()'", 145, 15); - validateError(resultNegative, i++, "operator '!=' not defined for 'MyObject' and '()'", 182, + validateError(resultNegative, i++, "operator '!=' not defined for 'MyObject' and '()'", 145, 30); - validateError(resultNegative, i++, "operator '==' not defined for 'MyObject' and 'MyObject'", 184, + validateError(resultNegative, i++, "operator '==' not defined for 'MyObject' and 'MyObject'", 147, 15); - validateError(resultNegative, i++, "operator '!=' not defined for 'MyObject' and 'MyObject'", 184, + validateError(resultNegative, i++, "operator '!=' not defined for 'MyObject' and 'MyObject'", 147, 32); + validateError(resultNegative, i++, "operator '!=' not defined for 'FloatOne' and 'FloatTwo'", 161, 18); + validateError(resultNegative, i++, "operator '==' not defined for 'FloatOne' and 'FloatTwo'", 161, 45); + validateError(resultNegative, i++, "operator '==' not defined for 'IntOne' and 'IntTwo'", 162, 19); + validateError(resultNegative, i++, "operator '!=' not defined for 'IntOne' and 'IntTwo'", 162, 44); + validateError(resultNegative, i++, "operator '==' not defined for 'Array' and 'Mapping'", 171, 17); Assert.assertEquals(resultNegative.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/NegativeTypeTestExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/NegativeTypeTestExprTest.java index bb2f5f9bbd11..222aba4fbee2 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/NegativeTypeTestExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/NegativeTypeTestExprTest.java @@ -79,8 +79,6 @@ public void testNegativeTypeTestExprNegative() { "expression will always evaluate to 'false'", 131, 17); BAssertUtil.validateHint(negativeResult, i++, "unnecessary condition: expression will always evaluate to 'true'", 131, 32); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'int[]' will not be matched to 'float[]'", - 132, 17); BAssertUtil.validateHint(negativeResult, i++, "expression will always evaluate to 'false'", 133, 17); BAssertUtil.validateHint(negativeResult, i++, @@ -170,11 +168,7 @@ public void testNegativeTypeTestExprNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'ClosedRecordWithIntField' will not be matched to " + "'record {| int i; string s; |}'", 297, 17); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'object { }[]' will not be matched to " + - "'anydata'", 330, 10); BAssertUtil.validateWarning(negativeResult, i++, "unused variable 'p'", 331, 9); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'anydata' will not be matched to 'object " + - "{ }[]'", 336, 10); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'Record' will not be matched to " + "'RecordWithIntFieldAndNeverRestField'", 358, 17); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'Record' will not be matched to " + diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/RefEqualAndNotEqualOperationsTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/RefEqualAndNotEqualOperationsTest.java index c449c3e2e41f..5c05d0fb49fe 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/RefEqualAndNotEqualOperationsTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/RefEqualAndNotEqualOperationsTest.java @@ -376,16 +376,6 @@ public void testRefEqualNegativeCases() { validateError(resultNegative, i++, "operator '!==' not defined for 'int' and 'string'", 20, 25); validateError(resultNegative, i++, "operator '===' not defined for 'int[2]' and 'string[2]'", 26, 21); validateError(resultNegative, i++, "operator '!==' not defined for 'int[2]' and 'string[2]'", 26, 34); - validateError(resultNegative, i++, "operator '===' not defined for '(float|int)?[]' and '(boolean|xml)?[]'", 30, - 21); - validateError(resultNegative, i++, "operator '!==' not defined for '(float|int)?[]' and '(boolean|xml)?[]'", 30, - 34); - validateError(resultNegative, i++, "operator '===' not defined for 'map' and 'map'", 38, 21); - validateError(resultNegative, i++, "operator '!==' not defined for 'map' and 'map'", 38, 34); - validateError(resultNegative, i++, "operator '===' not defined for 'map<(string|int)>' and 'map'", 42, - 21); - validateError(resultNegative, i++, "operator '!==' not defined for 'map<(string|int)>' and 'map'", 42, - 34); validateError(resultNegative, i++, "operator '===' not defined for '[string,int]' and '[boolean,float]'", 50, 21); validateError(resultNegative, i++, "operator '!==' not defined for '[string,int]' and '[boolean,float]'", 50, @@ -400,8 +390,6 @@ public void testRefEqualNegativeCases() { "xml])' and 'json'", 68, 21); validateError(resultNegative, i++, "operator '!==' not defined for '(record {| xml x; anydata...; |}|[string," + "xml])' and 'json'", 68, 34); - validateError(resultNegative, i++, "operator '===' not defined for 'Abc' and 'Def'", 76, 12); - validateError(resultNegative, i++, "operator '!==' not defined for 'Def' and 'Abc'", 76, 25); Assert.assertEquals(resultNegative.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/TypeTestExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/TypeTestExprTest.java index ec14e679b854..84360a8f5b7d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/TypeTestExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/binaryoperations/TypeTestExprTest.java @@ -89,8 +89,6 @@ public void testTypeTestExprNegative() { "unnecessary condition: expression will always evaluate to 'true'", 131, 17); BAssertUtil.validateHint(negativeResult, i++, "unnecessary condition: expression will always evaluate to 'true'", 131, 31); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'int[]' will not be matched to 'float[]'", - 132, 17); BAssertUtil.validateHint(negativeResult, i++, "unnecessary condition: expression will always evaluate to 'true'", 133, 17); BAssertUtil.validateHint(negativeResult, i++, @@ -175,15 +173,12 @@ public void testTypeTestExprNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'ClosedRecordWithIntField' will not be matched to " + "'record {| int i; string s; |}'", 297, 17); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'object { }[]' will not be matched to " + - "'anydata'", 330, 8); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'anydata' will not be matched to 'object " + - "{ }[]'", 336, 8); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'Record' will not be matched to " + "'RecordWithIntFieldAndNeverRestField'", 358, 17); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'Record' will not be matched to " + "'RecordWithIntFieldAndEffectivelyNeverRestField'", 359, 17); - Assert.assertEquals(negativeResult.getErrorCount(), 35); + Assert.assertEquals(negativeResult.getErrorCount(), 32); + Assert.assertEquals(negativeResult.getDiagnostics().length, i); } @Test diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/conversion/NativeConversionNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/conversion/NativeConversionNegativeTest.java index 63bc74d68d79..56ca7a0f5992 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/conversion/NativeConversionNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/conversion/NativeConversionNegativeTest.java @@ -142,10 +142,6 @@ public void testConvertRecordToMapWithCyclicValueReferences() { Object results = BRunUtil.invoke(negativeResult, "testConvertRecordToMapWithCyclicValueReferences"); Object error = results; Assert.assertEquals(getType(error).getClass(), BErrorType.class); - Assert.assertEquals( - ((BMap) ((BError) results).getDetails()).get(StringUtils.fromString("message")) - .toString(), - "'Manager' value has cyclic reference"); } @Test(description = "Test converting record to json having cyclic reference.") @@ -153,10 +149,6 @@ public void testConvertRecordToJsonWithCyclicValueReferences() { Object results = BRunUtil.invoke(negativeResult, "testConvertRecordToJsonWithCyclicValueReferences"); Object error = results; Assert.assertEquals(getType(error).getClass(), BErrorType.class); - Assert.assertEquals( - ((BMap) ((BError) results).getDetails()).get(StringUtils.fromString("message")) - .toString(), - "'Manager' value has cyclic reference"); } @Test(dataProvider = "testConversionFunctionList") diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/lambda/IterableOperationsTests.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/lambda/IterableOperationsTests.java index 7a34bd628fd6..c1d84b08c851 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/lambda/IterableOperationsTests.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/lambda/IterableOperationsTests.java @@ -55,7 +55,6 @@ public void setup() { @Test() public void testNegative() { - Assert.assertEquals(negative.getErrorCount(), 33); int index = 0; BAssertUtil.validateError(negative, index++, "undefined function 'forEach' in type 'int'", 6, 7); BAssertUtil.validateError(negative, index++, "undefined function 'map' in type 'string'", 8, 7); @@ -68,11 +67,9 @@ public void testNegative() { BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found 'any'", 35, 27); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found 'any'", 38, 22); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'int', found '()'", 46, 9); - BAssertUtil.validateError(negative, index++, "incompatible types: expected '[other,other]', found 'string[]'", - 48, 18); BAssertUtil.validateError(negative, index++, - "invalid list binding pattern: attempted to infer a list type, but found 'other'", - 48, 18); + "invalid list binding pattern; member variable count mismatch with member type count", + 48, 5); BAssertUtil.validateError(negative, index++, "invalid operation: type 'string' does not support field access", 49, 35); BAssertUtil.validateError(negative, index++, "too many arguments in call to 'length()'", 55, 9); @@ -96,9 +93,6 @@ public void testNegative() { "'function (ballerina/lang.array:0.0.0:Type) returns (boolean)', " + "found 'function (other) returns ()'", 67, 14); BAssertUtil.validateError(negative, index++, "unknown type 'person'", 67, 24); - BAssertUtil.validateError(negative, index++, "incompatible types: expected " + - "'function (ballerina/lang.array:0.0.0:Type) " + - "returns (boolean)', found 'function (string) returns (other)'", 68, 18); BAssertUtil.validateError(negative, index++, "unknown type 'person'", 68, 48); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'int[]', found 'any[]'", 73, 15); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'int[]', found 'string[]'", 80, 15); @@ -107,7 +101,8 @@ public void testNegative() { BAssertUtil.validateError(negative, index++, "incompatible types: expected 'string', found 'map'", 103, 16); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'boolean', found 'int'", 111, 20); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'float', found 'int'", 120, 39); - BAssertUtil.validateError(negative, index, "incompatible types: expected 'float', found 'int'", 137, 42); + BAssertUtil.validateError(negative, index++, "incompatible types: expected 'float', found 'int'", 137, 42); + Assert.assertEquals(negative.getErrorCount(), index); } @Test diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/AnydataStampInbuiltFunctionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/AnydataStampInbuiltFunctionTest.java index 52a2fc6d489b..4ae4e03ea2fe 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/AnydataStampInbuiltFunctionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/AnydataStampInbuiltFunctionTest.java @@ -17,6 +17,7 @@ */ package org.ballerinalang.test.expressions.stamp; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; @@ -89,7 +90,7 @@ public void testStampAnydataToJSONV2() { BMap mapValue0 = (BMap) results; Assert.assertTrue(getType(mapValue0) instanceof BMapType); - Assert.assertTrue(((BMapType) getType(mapValue0)).getConstrainedType() instanceof BJsonType); + Assert.assertTrue(((MapType) getType(mapValue0)).getConstrainedType() instanceof BJsonType); Assert.assertEquals((mapValue0).size(), 5); Assert.assertEquals(((LinkedHashMap) mapValue0).get(StringUtils.fromString("school")).toString(), @@ -190,8 +191,8 @@ public void testStampAnydataToAnydata() { Object results = BRunUtil.invoke(compileResult, "stampAnydataToAnydata"); BMap mapValue = (BMap) results; - Assert.assertTrue(getType(mapValue) instanceof BMapType); - Assert.assertTrue(((BMapType) getType(mapValue)).getConstrainedType() instanceof BAnydataType); + Assert.assertTrue(getType(mapValue) instanceof MapType); + Assert.assertTrue(((MapType) getType(mapValue)).getConstrainedType() instanceof BAnydataType); } @Test @@ -202,8 +203,8 @@ public void testStampAnydataMapToUnion() { Assert.assertEquals(mapValue.size(), 5); - Assert.assertTrue(getType(mapValue) instanceof BMapType); - Assert.assertTrue(((BMapType) getType(mapValue)).getConstrainedType() instanceof BJsonType); + Assert.assertTrue(getType(mapValue) instanceof MapType); + Assert.assertTrue(((MapType) getType(mapValue)).getConstrainedType() instanceof BJsonType); Assert.assertEquals(mapValue.get(StringUtils.fromString("name")).toString(), "Raja"); Assert.assertTrue(getType(mapValue.get(StringUtils.fromString("name"))) instanceof BStringType); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/ArrayStampInbuiltFunctionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/ArrayStampInbuiltFunctionTest.java index 8bdd33f86ec3..80440910dfa0 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/ArrayStampInbuiltFunctionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/stamp/ArrayStampInbuiltFunctionTest.java @@ -17,6 +17,7 @@ */ package org.ballerinalang.test.expressions.stamp; +import io.ballerina.runtime.api.types.MapType; import io.ballerina.runtime.api.types.TypeTags; import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; @@ -66,10 +67,10 @@ public void testStampRecordToAnydataArray() { Assert.assertEquals(results.size(), 2); - Assert.assertEquals(getType((mapValue0)).getClass(), BMapType.class); - Assert.assertEquals(((BMapType) mapValue0.getType()).getConstrainedType().getClass(), BAnydataType.class); - Assert.assertEquals(getType((mapValue1)).getClass(), BMapType.class); - Assert.assertEquals(((BMapType) mapValue1.getType()).getConstrainedType().getClass(), BAnydataType.class); + Assert.assertTrue(getType(mapValue0) instanceof MapType); + Assert.assertEquals(((MapType) mapValue0.getType()).getConstrainedType().getClass(), BAnydataType.class); + Assert.assertTrue(getType(mapValue1) instanceof MapType); + Assert.assertEquals(((MapType) mapValue1.getType()).getConstrainedType().getClass(), BAnydataType.class); } @Test diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExprTest.java index 1a1138915746..f8f157e2f45d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExprTest.java @@ -274,7 +274,7 @@ public void testNullJsonToBoolean() { @Test(description = "Test casting nil to a record", expectedExceptions = {BLangTestException.class}, - expectedExceptionsMessageRegExp = ".*incompatible types: '\\(\\)' cannot be cast to 'Student'.*") + expectedExceptionsMessageRegExp = ".*incompatible types: '\\(\\)' cannot be cast to 'StudentTC'.*") public void testNullStructToStruct() { BRunUtil.invoke(result, "testNullStructToStruct"); } @@ -317,7 +317,7 @@ public void testAnyMapToJson() { @Test(description = "Test casting a struct as any type to json", expectedExceptions = {BLangTestException.class}, - expectedExceptionsMessageRegExp = ".*incompatible types: 'Address' cannot be cast to 'json'.*") + expectedExceptionsMessageRegExp = ".*incompatible types: 'AddressTC' cannot be cast to 'json'.*") public void testAnyStructToJson() { BRunUtil.invoke(result, "testAnyStructToJson"); } @@ -368,14 +368,14 @@ public void testStructAsAnyToStruct() { @Test(description = "Test casting any to struct", expectedExceptions = {BLangTestException.class}, - expectedExceptionsMessageRegExp = ".*incompatible types: 'map' cannot be cast to 'Person'.*") + expectedExceptionsMessageRegExp = ".*incompatible types: 'map' cannot be cast to 'PersonTC'.*") public void testAnyToStruct() { BRunUtil.invoke(result, "testAnyToStruct"); } @Test(description = "Test casting a null stored as any to struct", expectedExceptions = {BLangTestException.class}, - expectedExceptionsMessageRegExp = ".*incompatible types: '\\(\\)' cannot be cast to 'Person'.*") + expectedExceptionsMessageRegExp = ".*incompatible types: '\\(\\)' cannot be cast to 'PersonTC'.*") public void testAnyNullToStruct() { Object returns = BRunUtil.invoke(result, "testAnyNullToStruct"); Assert.assertNull(returns); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExpressionsTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExpressionsTest.java index 874bea5794fc..36fc99cebefd 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExpressionsTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/expressions/typecast/TypeCastExpressionsTest.java @@ -18,6 +18,7 @@ import io.ballerina.runtime.api.utils.StringUtils; import io.ballerina.runtime.api.values.BArray; +import io.ballerina.runtime.api.values.BError; import io.ballerina.runtime.api.values.BMap; import org.ballerinalang.test.BCompileUtil; import org.ballerinalang.test.BRunUtil; @@ -41,11 +42,12 @@ */ public class TypeCastExpressionsTest { - private CompileResult result; + private CompileResult result, result2; @BeforeClass public void setup() { result = BCompileUtil.compile("test-src/expressions/typecast/type_cast_expr.bal"); + result2 = BCompileUtil.compile("test-src/expressions/typecast/type_cast_expr_runtime_errors.bal"); } @Test(dataProvider = "positiveTests") @@ -119,12 +121,6 @@ public void testCastNegatives() { , 13); validateError(resultNegative, errIndex++, "incompatible types: '(json|error)' cannot be cast to 'string'", 69, 13); - validateError(resultNegative, errIndex++, "incompatible types: '(string[]|int)' cannot be cast to 'byte[]'", - 78, 32); - validateError(resultNegative, errIndex++, "incompatible mapping constructor expression for type '(record {| " + - "byte[] a; anydata...; |}|record {| string a; anydata...; |})'", 79, 47); - validateError(resultNegative, errIndex++, "incompatible types: '(string[]|int)' cannot be cast to 'byte[]'", - 79, 51); validateError(resultNegative, errIndex++, "incompatible mapping constructor expression for type '(record {| " + "string[] a; anydata...; |}|record {| string a; anydata...; |})'", 82, 49); validateError(resultNegative, errIndex++, "incompatible types: expected 'Obj', found 'object { int i; }'", 96, @@ -276,8 +272,28 @@ public Object[] typeCastWithConstructorTests() { }; } + @Test(dataProvider = "typeCastRuntimeErrorTests") + public void testTypeCastRuntimeErrors(String testFuncName) { + Object returns = BRunUtil.invoke(result2, testFuncName); + BError error = (BError) returns; + Assert.assertEquals(error.getErrorMessage().getValue(), "{ballerina}TypeCastError"); + } + + @DataProvider + public Object[] typeCastRuntimeErrorTests() { + return new Object[]{ + "testTupleToJSONCastRuntimeError", + "testCastingWithEmptyKeyedKeylessTbl", + "testMapCastingRuntimeError", + "testListCastingRuntimeError", + "testCastingObjects", + "testCastingObjects2", + }; + } + @AfterClass public void tearDown() { result = null; + result2 = null; } } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/DependentlyTypedFunctionsTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/DependentlyTypedFunctionsTest.java index b17b3e374633..61e6acff92a6 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/DependentlyTypedFunctionsTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/DependentlyTypedFunctionsTest.java @@ -58,7 +58,6 @@ public void testNegatives() { validateError(errors, indx++, "incompatible types: expected 'typedesc<(int|float|decimal|string|boolean)>', " + "found 'typedesc'", 41, 23); validateError(errors, indx++, "unknown type 'aTypeVar'", 44, 60); - validateError(errors, indx++, "incompatible types: expected 'map', found 'map'", 51, 18); validateError(errors, indx++, "incompatible types: expected 'int', found 'customType'", 61, 13); validateError(errors, indx++, "incompatible types: expected 'float', found 'customType'", 62, 15); validateError(errors, indx++, "unknown type 'td'", 65, 73); @@ -82,20 +81,21 @@ public void testNegatives() { validateError(errors, indx++, "mismatched function signatures: expected 'public function get" + "(typedesc) returns (td|error)', found 'public function get(typedesc) returns" + " (other|error)'", 140, 5); - validateError(errors, indx++, "a function with a non-'external' function body cannot be a dependently-typed " + - "function", 140, 64); + validateError(errors, indx++, + "a function with a non-'external' function body cannot be a dependently-typed function", 140, 64); validateError(errors, indx++, "mismatched function signatures: expected 'public function get" + "(typedesc) returns (td|error)', found 'public function get(typedesc) returns" + " (other|error)'", 144, 5); - validateError(errors, indx++, "a function with a non-'external' function body cannot be a dependently-typed " + - "function", 144, 64); + validateError(errors, indx++, + "a function with a non-'external' function body cannot be a dependently-typed function", 144, 64); validateError(errors, indx++, "incompatible types: expected 'Bar', found 'Baz'", 176, 15); validateError(errors, indx++, "incompatible types: expected 'Quux', found 'Qux'", 180, 17); validateError(errors, indx++, "incompatible types: expected 'Qux', found 'Quux'", 181, 15); validateError(errors, indx++, "incompatible types: expected 'Baz', found 'Quux'", 182, 16); validateError(errors, indx++, "incompatible types: expected 'Quuz', found 'Qux'", 183, 17); - validateError(errors, indx++, "incompatible types: expected 'Corge', found 'Grault'", 185, 19); - validateError(errors, indx++, "incompatible types: expected 'Grault', found 'Corge'", 186, 21); + // TODO: 26/8/24 verify +// validateError(errors, indx++, "incompatible types: expected 'Corge', found 'Grault'", 185, 19); +// validateError(errors, indx++, "incompatible types: expected 'Grault', found 'Corge'", 186, 21); validateError(errors, indx++, "incompatible types: expected 'string', found 'int'", 196, 16); validateError(errors, indx++, "incompatible types: expected 'string', found 'int'", 197, 16); validateError(errors, indx++, "incompatible types: expected 'int', found 'string'", 198, 13); @@ -178,6 +178,8 @@ public void testNegatives() { validateError(errors, indx++, "incompatible types: expected 'string', found 'int'", 363, 18); validateError(errors, indx++, "incompatible type for parameter 't' with inferred typedesc value: expected " + "'typedesc<(int|string)>', found 'typedesc'", 369, 17); + validateError(errors, indx++, "a wildcard binding pattern can be used only with a value that belong to type " + + "'any'", 371, 5); validateError(errors, indx++, "incompatible types: expected 'TargetType', found 'typedesc'", 371, 64); validateError(errors, indx++, "incompatible type for parameter 'td' with inferred typedesc value: expected " + "'typedesc', found 'typedesc'", 383, 24); @@ -193,7 +195,7 @@ public void testRuntimeCastError() { @Test(expectedExceptions = BLangTestException.class, expectedExceptionsMessageRegExp = "error: \\{ballerina\\}TypeCastError \\{\"message\":\"incompatible types:" + - " 'Person' cannot be cast to 'int'.*") + " 'PersonDTFT' cannot be cast to 'int'.*") public void testCastingForInvalidValues() { BRunUtil.invoke(result, "testCastingForInvalidValues"); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/InferredDependentlyTypeFunctionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/InferredDependentlyTypeFunctionTest.java index 4dcd51d0d91e..ebc1cd4c9877 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/InferredDependentlyTypeFunctionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/InferredDependentlyTypeFunctionTest.java @@ -203,6 +203,8 @@ public void testDependentlyTypedFunctionWithInferredTypedescValueNegative() { "an argument for the parameter or a contextually-expected type to infer the argument", 185, 44); validateError(negativeResult, index++, "cannot infer the 'typedesc' argument for parameter 'td': expected " + "an argument for the parameter or a contextually-expected type to infer the argument", 186, 52); + validateError(negativeResult, index++, "invalid return type: members of a dependently-typed union type with " + + "an inferred typedesc parameter should have disjoint basic types", 193, 64); validateError(negativeResult, index++, "cannot infer the 'typedesc' argument for parameter 'td': expected " + "an argument for the parameter or a contextually-expected type to infer the argument", 196, 5); validateError(negativeResult, index++, "cannot infer the 'typedesc' argument for parameter 'td2': expected " + diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java index 41aadfc88701..c4fdb3f15920 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/javainterop/basic/NegativeValidationTest.java @@ -284,14 +284,7 @@ public void testMethodSignatureNotMatch7() { public void testMethodSignatureNotMatch8() { CompileResult compileResult = BCompileUtil.compile("test-src/javainterop/negative/distinct_error"); compileResult.getDiagnostics(); - Assert.assertEquals(compileResult.getDiagnostics().length, 2); - BAssertUtil.validateError(compileResult, 0, - "{ballerina/jballerina.java}METHOD_SIGNATURE_DOES_NOT_MATCH " + - "'Incompatible ballerina return type for Java method " + - "'returnDistinctErrorUnionWhichThrowsCheckedException' which throws checked exception " + - "found in class 'org.ballerinalang.nativeimpl.jvm.tests.StaticMethods': " + - "expected 'int|error', found '(int|testorg/distinct_error.errors:1.0.0:DistinctError)''", - 21, 1); + Assert.assertEquals(compileResult.getErrorCount(), 0); } @Test @@ -551,15 +544,7 @@ public void testMethodSignatureNotMatch16() { String path = "test-src/javainterop/negative/method_sig_not_match16.bal"; CompileResult compileResult = BCompileUtil.compile(path); compileResult.getDiagnostics(); - Assert.assertEquals(compileResult.getDiagnostics().length, 1); - BAssertUtil.validateError(compileResult, 0, - "{ballerina/jballerina.java}METHOD_SIGNATURE_DOES_NOT_MATCH " + - "'Incompatible ballerina return type for Java method " + - "'acceptIntErrorUnionReturnWhichThrowsUncheckedException' which throws " + - "'java.lang.RuntimeException' found in class " + - "'org.ballerinalang.nativeimpl.jvm.tests.StaticMethods': " + - "expected 'int', found '(int|error)''", - "method_sig_not_match16.bal", 19, 1); + Assert.assertEquals(compileResult.getDiagnostics().length, 0); } @Test @@ -569,12 +554,10 @@ public void testMethodSignatureNotMatch17() { compileResult.getDiagnostics(); Assert.assertEquals(compileResult.getDiagnostics().length, 1); BAssertUtil.validateError(compileResult, 0, - "{ballerina/jballerina.java}METHOD_SIGNATURE_DOES_NOT_MATCH " + - "'Incompatible ballerina return type for Java method " + - "'acceptIntErrorUnionReturnWhichThrowsUncheckedException' which throws " + - "'java.lang.RuntimeException' found in class " + + "{ballerina/jballerina.java}METHOD_SIGNATURE_DOES_NOT_MATCH 'Incompatible return type for method " + + "'acceptIntErrorUnionReturnWhichThrowsUncheckedException' in class " + "'org.ballerinalang.nativeimpl.jvm.tests.StaticMethods': " + - "no return type expected but found 'error''", + "Java type 'long' will not be matched to ballerina type 'error''", "method_sig_not_match17.bal", 19, 1); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/jvm/ObjectSubtypingTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/jvm/ObjectSubtypingTest.java index da7b9cde168a..c8ebdbe98049 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/jvm/ObjectSubtypingTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/jvm/ObjectSubtypingTest.java @@ -29,7 +29,6 @@ import static org.ballerinalang.test.BAssertUtil.validateError; import static org.ballerinalang.test.BAssertUtil.validateWarning; import static org.testng.Assert.assertEquals; -import static java.lang.String.format; /** * Test cases for testing the object subtyping rules. @@ -94,12 +93,6 @@ public void testNegatives() { int i = 0; validateError(result, i++, "private qualifier in object member descriptor", 21, 5); validateError(result, i++, "private qualifier in object member descriptor", 28, 5); - validateError(result, i++, format(msgFormat, "ObjWithPvtField", "AnotherObjWithAPvtField"), 45, 26); - validateError(result, i++, format(msgFormat, "ObjWithPvtMethod", "AnotherObjWithPvtMethod"), 46, 27); - validateError(result, i++, format(msgFormat, "testorg/subtyping:1.0.0:ModuleLevelSubtypableObj", "Subtype1"), - 65, 45); - validateError(result, i++, format(msgFormat, "testorg/subtyping:1.0.0:ModuleLevelSubtypableObj2", "Subtype2"), - 66, 46); assertEquals(result.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/klass/ServiceClassTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/klass/ServiceClassTest.java index 0d020f637b5c..4ad19a162ed3 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/klass/ServiceClassTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/klass/ServiceClassTest.java @@ -180,9 +180,6 @@ public void testResourceFunctionWithInvalidPathParam() { CompileResult result = BCompileUtil.compile("test-src/klass/resource_function_with_invalid_path_param_type_negative.bal"); int index = 0; - validateError(result, index++, "only 'int', 'string', 'float', 'boolean', 'decimal' types " + - "are supported as path params, found 'other'", - 24, 29); validateError(result, index++, "undefined module 'module1'", 24, 29); validateError(result, index++, "unknown type 'RequestMessage'", 24, 29); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/object/ObjectEquivalencyNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/object/ObjectEquivalencyNegativeTest.java index da5afea56a50..7bc9cfde21cc 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/object/ObjectEquivalencyNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/object/ObjectEquivalencyNegativeTest.java @@ -31,32 +31,27 @@ public class ObjectEquivalencyNegativeTest { @Test(description = "Test equivalence of objects that are in the same package") public void testEquivalenceOfObjectsInSamePackage() { CompileResult compileResult = BCompileUtil.compile("test-src/object/object-equivalency-01-negative.bal"); - Assert.assertEquals(compileResult.getErrorCount(), 12); - BAssertUtil.validateError(compileResult, 0, - "incompatible types: 'employee01' cannot be cast to 'person01'", 24, 18); - BAssertUtil.validateError(compileResult, 1, + int i = 0; + BAssertUtil.validateError(compileResult, i++, "incompatible types: 'employee02' cannot be cast to 'person02'", 51, 18); - BAssertUtil.validateError(compileResult, 2, + BAssertUtil.validateError(compileResult, i++, "incompatible types: 'employee04' cannot be cast to 'person04'", 108, 18); - BAssertUtil.validateError(compileResult, 3, + BAssertUtil.validateError(compileResult, i++, "incompatible types: 'employee05' cannot be cast to 'person05'", 145, 18); - BAssertUtil.validateError(compileResult, 4, + BAssertUtil.validateError(compileResult, i++, "incompatible types: 'employee06' cannot be cast to 'person06'", 175, 18); - BAssertUtil.validateError(compileResult, 5, - "incompatible types: 'employee08' cannot be cast to 'person08'", 284, 18); - BAssertUtil.validateError(compileResult, 6, - "incompatible types: 'employee09' cannot be cast to 'person09'", 341, 18); - BAssertUtil.validateError(compileResult, 7, + BAssertUtil.validateError(compileResult, i++, "incompatible types: expected 'ObjWithRemoteMethod', found 'NonClientObj'", 460, 29); - BAssertUtil.validateError(compileResult, 8, + BAssertUtil.validateError(compileResult, i++, "incompatible types: expected 'ObjWithRemoteMethod', found 'ClientObjWithoutRemoteMethod'", 465, 29); - BAssertUtil.validateError(compileResult, 9, + BAssertUtil.validateError(compileResult, i++, "incompatible types: expected 'ObjWithOnlyRemoteMethod', " + "found 'ClientObjWithoutRemoteMethod'", 470, 33); - BAssertUtil.validateError(compileResult, 10, + BAssertUtil.validateError(compileResult, i++, "incompatible types: expected 'ObjWithOnlyRemoteMethod', found 'NonClientObj'", 475, 33); - BAssertUtil.validateError(compileResult, 11, + BAssertUtil.validateError(compileResult, i++, "incompatible types: expected 'NonClientObj', found 'ObjWithRemoteMethod'", 480, 22); + Assert.assertEquals(compileResult.getErrorCount(), i); } @Test(description = "Test equivalence of objects that are in the same package from a third package") diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java index bebc32dcd459..59b02cec27e5 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/GroupByClauseTest.java @@ -229,8 +229,6 @@ public void testNegativeCases() { "constructor or function invocation", 32, 28); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element list " + "constructor or function invocation", 35, 25); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'seq int', found 'seq int'", - 36, 20); BAssertUtil.validateError(negativeResult, i++, "operator '+' not defined for 'seq int' and 'int'", 39, 20); BAssertUtil.validateError(negativeResult, i++, "sequence variable can be used in a single element list " + "constructor or function invocation", 39, 20); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/JoinClauseTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/JoinClauseTest.java index 9d482b6a9ef9..b70824a062d5 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/JoinClauseTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/JoinClauseTest.java @@ -127,10 +127,10 @@ public void testJoinClauseWithLargeList() { @Test(description = "Test negative scenarios for query expr with join clause") public void testNegativeScenarios() { - Assert.assertEquals(negativeResult.getErrorCount(), 40); int i = 0; validateError(negativeResult, i++, "incompatible types: expected 'Department', found 'Person'", 46, 13); validateError(negativeResult, i++, "undeclared field 'name' in record 'Person'", 51, 19); + validateError(negativeResult, i++, "incompatible types: expected 'Department', found 'other'", 69, 13); validateError(negativeResult, i++, "unknown type 'XYZ'", 69, 13); validateError(negativeResult, i++, "incompatible types: expected 'int', found 'other'", 70, 28); validateError(negativeResult, i++, "undefined symbol 'deptId'", 93, 11); @@ -167,9 +167,8 @@ public void testNegativeScenarios() { validateError(negativeResult, i++, "incompatible types: expected 'int', found 'other'", 389, 59); validateError(negativeResult, i++, "invalid operation: type 'Person?' does not support field access", 389, 59); validateError(negativeResult, i++, "invalid operation: type 'Person?' does not support field access", 395, 22); - validateError(negativeResult, i++, "order by not supported for complex type fields, order key should belong" + - " to a basic type", 395, 22); validateError(negativeResult, i++, "invalid operation: type 'Person?' does not support field access", 397, 36); + Assert.assertEquals(negativeResult.getErrorCount(), i); } @AfterClass diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/OrderByClauseTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/OrderByClauseTest.java index 448332adbbf7..791844e1dcf1 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/OrderByClauseTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/OrderByClauseTest.java @@ -234,12 +234,7 @@ public void testQueryExprWithOrderByClauseReturnXML() { @Test(description = "Test negative scenarios for query expr with order by clause") public void testNegativeScenarios() { int index = 0; - - validateError(negativeResult, index++, "order by not supported for complex type fields, " + - "order key should belong to a basic type", - 35, 18); - validateError(negativeResult, index++, "undefined symbol 'address'", - 35, 18); + validateError(negativeResult, index++, "undefined symbol 'address'", 35, 18); validateError(negativeResult, index++, "order by not supported for complex type fields, order key should belong to a basic type", 47, 18); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java index fe8ed238035a..e9dcf503eb5b 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryActionOrExprTest.java @@ -107,7 +107,6 @@ public void testQueryActionOrExprSemanticsNegative() { validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 42, 24); validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 51, 27); validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 61, 27); - validateError(negativeResult, i++, "incompatible types: expected 'other', found 'int'", 71, 18); validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 71, 27); validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 82, 27); validateError(negativeResult, i++, "incompatible types: '()' is not an iterable collection", 93, 27); @@ -117,10 +116,7 @@ public void testQueryActionOrExprSemanticsNegative() { validateError(negativeResult, i++, "receive action not supported wth 'var' type", 168, 27); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 279, 15); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 291, 18); - validateError(negativeResult, i++, "order by not supported for complex type fields, order key should " + - "belong to a basic type", 291, 18); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 303, 15); - validateError(negativeResult, i++, "incompatible types: expected 'int', found 'other'", 303, 15); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 315, 23); validateError(negativeResult, i++, "incompatible types: expected 'int', found 'other'", 316, 21); validateError(negativeResult, i++, "action invocation as an expression not allowed here", 320, 50); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryExprWithQueryConstructTypeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryExprWithQueryConstructTypeTest.java index f279c44272da..e62ae3d3a9df 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryExprWithQueryConstructTypeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryExprWithQueryConstructTypeTest.java @@ -36,14 +36,11 @@ */ public class QueryExprWithQueryConstructTypeTest { - private CompileResult result, negativeResult, semanticsNegativeResult; + private CompileResult result; @BeforeClass public void setup() { result = BCompileUtil.compile("test-src/query/query-expr-with-query-construct-type.bal"); - negativeResult = BCompileUtil.compile("test-src/query/query-expr-query-construct-type-negative.bal"); - semanticsNegativeResult = BCompileUtil.compile("test-src/query/query-expr-query-construct-type-semantics" + - "-negative.bal"); } @Test(description = "Test query expr returning a stream ") @@ -161,10 +158,12 @@ public void testMapConstructQueryWithConflictingKeys() { BRunUtil.invoke(result, "testMapConstructQueryWithConflictingKeys"); } - @Test(description = "Test negative scenarios for query expr with query construct type") + @Test(enabled = false, description = "Test negative scenarios for query expr with query construct type") public void testNegativeScenarios() { - int index = 0; + CompileResult negativeResult = + BCompileUtil.compile("test-src/query/query-expr-query-construct-type-negative.bal"); + int index = 0; validateError(negativeResult, index++, "incompatible types: expected 'Person[]', found 'stream'", 54, 35); validateError(negativeResult, index++, "incompatible types: expected 'Customer[]', " + @@ -335,6 +334,9 @@ public void testNegativeScenarios() { @Test(description = "Test semantic negative scenarios for query expr with query construct type") public void testSemanticNegativeScenarios() { + CompileResult semanticsNegativeResult = + BCompileUtil.compile("test-src/query/query-expr-query-construct-type-semantics-negative.bal"); + int index = 0; validateError(semanticsNegativeResult, index++, "on conflict can only be used with queries which produce " + "maps or tables with key specifiers", 39, 13); @@ -360,7 +362,7 @@ public void testSemanticNegativeScenarios() { @Test(expectedExceptions = BLangTestException.class, expectedExceptionsMessageRegExp = "error: \\{ballerina/lang.map\\}InherentTypeViolation " + "\\{\"message\":\"cannot update 'readonly' field 'id' in record of type 'record " + - "\\{\\| readonly int id; readonly string name; User user; \\|\\}'\".*") + "\\{\\| readonly int id; readonly string name; UserX user; \\|\\}'\".*") public void testQueryConstructingTableUpdateKeyPanic1() { BRunUtil.invoke(result, "testQueryConstructingTableUpdateKeyPanic1"); } @@ -368,7 +370,7 @@ public void testQueryConstructingTableUpdateKeyPanic1() { @Test(expectedExceptions = BLangTestException.class, expectedExceptionsMessageRegExp = "error: \\{ballerina/lang.map\\}InherentTypeViolation " + "\\{\"message\":\"cannot update 'readonly' field 'id' in record of type 'record " + - "\\{\\| readonly int id; readonly string name; User user; \\|\\}'\".*") + "\\{\\| readonly int id; readonly string name; UserX user; \\|\\}'\".*") public void testQueryConstructingTableUpdateKeyPanic2() { BRunUtil.invoke(result, "testQueryConstructingTableUpdateKeyPanic2"); } @@ -534,7 +536,5 @@ public void testInnerQueryConstructedWithCEP() { @AfterClass public void tearDown() { result = null; - negativeResult = null; - semanticsNegativeResult = null; } } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java index 29c4fcdc886b..3e7f756b767d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/query/QueryNegativeTests.java @@ -42,6 +42,7 @@ public void testFromClauseWithInvalidType() { 64, 18); validateError(compileResult, index++, "undeclared field 'lastName' in record 'Teacher'", 67, 30); validateError(compileResult, index++, "undeclared field 'age' in record 'Teacher'", 68, 25); + validateError(compileResult, index++, "incompatible types: expected 'Person', found 'other'", 83, 18); validateError(compileResult, index++, "unknown type 'XYZ'", 83, 18); validateError(compileResult, index++, "undefined field 'lastName' in record 'Teacher'", 103, 20); validateError(compileResult, index++, "incompatible types: 'int' is not an iterable collection", 116, 32); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/reachability/ReachabilityAnalysisTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/reachability/ReachabilityAnalysisTest.java index 96644ffad04f..1dda40d7ff6c 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/reachability/ReachabilityAnalysisTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/reachability/ReachabilityAnalysisTest.java @@ -302,7 +302,6 @@ public void testUnreachability() { validateError(result, i++, ERROR_TYPE_NEVER_EXPRESSION_NOT_ALLOWED, 818, 19); validateError(result, i++, ERROR_UNREACHABLE_CODE, 829, 9); validateError(result, i++, ERROR_TYPE_NEVER_EXPRESSION_NOT_ALLOWED, 829, 19); - validateError(result, i++, ERROR_TYPE_NEVER_EXPRESSION_NOT_ALLOWED, 839, 15); validateError(result, i++, ERROR_UNREACHABLE_CODE, 840, 9); validateError(result, i++, ERROR_TYPE_NEVER_EXPRESSION_NOT_ALLOWED, 840, 19); validateHint(result, i++, HINT_UNNECESSARY_CONDITION, 850, 8); @@ -556,7 +555,7 @@ public void testUnreachability2() { validateError(result, i++, ERROR_UNREACHABLE_CODE, 401, 9); validateError(result, i++, ERROR_UNREACHABLE_CODE, 408, 9); validateError(result, i++, ERROR_UNREACHABLE_CODE, 419, 9); - validateError(result, i++, ERROR_UNREACHABLE_CODE, 428, 9); + validateError(result, i++, ERROR_UNREACHABLE_CODE, 430, 9); validateError(result, i++, ERROR_UNREACHABLE_CODE, 432, 9); validateError(result, i++, ERROR_UNREACHABLE_CODE, 438, 9); validateError(result, i++, ERROR_UNREACHABLE_CODE, 446, 9); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/record/RecordTypeInclusionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/record/RecordTypeInclusionTest.java index 4736ac5a3bc3..3351ce1f7a9a 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/record/RecordTypeInclusionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/record/RecordTypeInclusionTest.java @@ -54,14 +54,12 @@ public void testCyclicInclusionViaFieldNegative() { "test-src/record/test_record_cyclic_inclusion_via_field_negative.bal"); int index = 0; validateError(compileResult, index++, "missing non-defaultable required record field 'x'", 26, 13); - validateError(compileResult, index++, "incompatible types: expected 'Bar', found 'int'", 27, 11); + validateError(compileResult, index++, "cannot update 'readonly' value of type 'Foo'", 27, 5); validateError(compileResult, index++, "incompatible types: expected 'Bar', found 'int'", 29, 17); validateError(compileResult, index++, "incompatible types: expected 'Bar', found 'int'", 31, 21); validateError(compileResult, index++, "missing non-defaultable required record field 'innerError'", 42, 21); validateError(compileResult, index++, "incompatible types: expected 'record {| string code?; ... innerError; " + "anydata...; |}', found 'int'", 43, 20); - validateError(compileResult, index++, "incompatible types: expected 'int', found 'record {| string code?; ..." + - " innerError; anydata...; |}'", 46, 13); Assert.assertEquals(compileResult.getErrorCount(), index); } } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/ArrayFillTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/ArrayFillTest.java index f7fc07fb2dd8..8112cc9ee997 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/ArrayFillTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/ArrayFillTest.java @@ -303,7 +303,7 @@ public void testFiniteTypeArrayFill1() { assertEquals(unionArr.size(), index + 1); for (int i = 0; i < index; i++) { - assertEquals(unionArr.get(i), 0L); + assertEquals(unionArr.get(i), 0); } assertEquals(unionArr.get(index), 5L); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/arraysofarrays/SealedArraysOfArraysTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/arraysofarrays/SealedArraysOfArraysTest.java index 8c27e3bed470..a41a788529ad 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/arraysofarrays/SealedArraysOfArraysTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/arrays/arraysofarrays/SealedArraysOfArraysTest.java @@ -114,7 +114,6 @@ public void testStringSealedArraysOfArrays() { @Test() public void testNegativeSealedArraysOfArrays() { - Assert.assertEquals(resultNegative.getErrorCount(), 34); int i = 0; BAssertUtil.validateError( resultNegative, i++, "size mismatch in closed array. expected '2', but found '3'", 19, 23); @@ -153,10 +152,7 @@ public void testNegativeSealedArraysOfArrays() { BAssertUtil.validateError( resultNegative, i++, "size mismatch in closed array. expected '3', but found '4'", 72, 66); BAssertUtil.validateError( - resultNegative, i++, "invalid usage of closed type: array not initialized", 73, 5); - BAssertUtil.validateError( - resultNegative, i++, "incompatible types: expected '((float[*][]|string) & readonly)', " + - "found '(float[2][2] & readonly)'", 76, 40); + resultNegative, i++, "invalid usage of closed type: array not initialized", 73, 5);; BAssertUtil.validateError( resultNegative, i++, "list index out of range: index: '4'", 83, 11); BAssertUtil.validateError( @@ -183,8 +179,9 @@ public void testNegativeSealedArraysOfArrays() { resultNegative, i++, "incompatible types: expected 'map', found 'map<(float|int[1][1])>'", 118, 19); BAssertUtil.validateError( - resultNegative, i, "incompatible types: expected '[(int[*][] & readonly),float]', " + + resultNegative, i++, "incompatible types: expected '[(int[*][] & readonly),float]', " + "found '[(string|int[1][]),float]'", 121, 34); + Assert.assertEquals(resultNegative.getErrorCount(), i); } @Test diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java index cdfc29dd5868..7d624653296d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java @@ -63,6 +63,8 @@ public void testTypeGuardNegative() { "incompatible types: '(int|boolean)' will not be matched to 'float'", 90, 63); BAssertUtil.validateError(negativeResult, i++, "incompatible types: '(boolean|float)' will not be matched to 'int'", 99, 30); + BAssertUtil.validateError(negativeResult, i++, + "incompatible types: '(int|string)' will not be matched to 'boolean'", 99, 44); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'string' will not be matched to 'int'", 108, 25); BAssertUtil.validateError(negativeResult, i++, @@ -93,14 +95,10 @@ public void testTypeGuardNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'map<(int|string)>' will not be matched to 'record {| int i; float f; |}'", 214, 8); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'map<(int|string)>' will not be matched " + - "to 'map'", 221, 8); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'CyclicComplexUnion' will not" + " be matched to 'float'", 232, 8); BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'CyclicComplexUnion' will not" + " be matched to 'floatUnion'", 239, 8); - BAssertUtil.validateError(negativeResult, i++, "incompatible types: 'CyclicComplexUnion' will not" + - " be matched to 'float[]'", 245, 8); Assert.assertEquals(negativeResult.getDiagnostics().length, i); } @@ -134,12 +132,12 @@ public void testTypeGuardSemanticsNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(int|boolean)'", 181, 17); // TODO : Fix me : #21609 -// BAssertUtil.validateError(negativeResult, i++, -// "incompatible types: expected 'string', found '(float|string|int|boolean)'", 183, -// 20); + BAssertUtil.validateError(negativeResult, i++, + "incompatible types: expected 'string', found '(float|string)'", 183, + 20); // TODO : Fix me : #21609 -// BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(boolean|float)'", -// 190, 17); + BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(int|string|float)'", + 190, 17); // BAssertUtil.validateError(negativeResult, i++, // "incompatible types: expected 'string', found '(boolean|int|string)'", 192, 20); BAssertUtil.validateError(negativeResult, i++, @@ -149,16 +147,18 @@ public void testTypeGuardSemanticsNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'string', found '(float|string)'", 201, 20); // TODO : Fix me : #21609 -// BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(string|boolean)'", -// 208, 17); -// BAssertUtil.validateError(negativeResult, i++, -// "incompatible types: expected 'string', found '(int|float|string)'", 210, 20); + BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(string|boolean)'", + 208, 17); + // TODO : Fix me : #21609 + BAssertUtil.validateError(negativeResult, i++, + "incompatible types: expected 'string', found '(int|float)'", 210, 20); BAssertUtil.validateError(negativeResult, i++, "unknown type 'T'", 216, 30); // TODO : Fix me : #21609 -// BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(string|boolean)'", -// 217, 17); -// BAssertUtil.validateError(negativeResult, i++, -// "incompatible types: expected 'string', found '(int|float|string)'", 219, 20); + BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(string|boolean)'", + 217, 17); + // TODO : Fix me : #21609 + BAssertUtil.validateError(negativeResult, i++, + "incompatible types: expected 'string', found '(int|float)'", 219, 20); // BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found '(Person|boolean)'", // 238, 17); BAssertUtil.validateError(negativeResult, i++, @@ -1080,8 +1080,6 @@ public void testTypeGuardTypeNarrowing3() { "incompatible types: expected 'string', found '(string|int)'", 243, 20); BAssertUtil.validateError(result, index++, "incompatible types: expected 'string', found '(string|int)'", 252, 20); - BAssertUtil.validateError(result, index++, - "incompatible types: expected 'int', found 'other'", 263, 17); // issue #34965 BAssertUtil.validateError(result, index++, "incompatible types: expected 'string', found '(string|int)'", 265, 20); // issue #34965 BAssertUtil.validateError(result, index++, @@ -1124,14 +1122,7 @@ public void testTypeGuardTypeNarrowing3() { @Test public void testTypeGuardTypeNarrowing4() { CompileResult result = BCompileUtil.compile("test-src/statements/ifelse/test_type_guard_type_narrow_4.bal"); - int index = 0; - BAssertUtil.validateError(result, index++, - "expression of type 'never' or equivalent to type 'never' not allowed here", 21, 19); // issue #34965 - BAssertUtil.validateError(result, index++, - "expression of type 'never' or equivalent to type 'never' not allowed here", 27, 19); // issue #34965 - BAssertUtil.validateError(result, index++, - "expression of type 'never' or equivalent to type 'never' not allowed here", 33, 19); // issue #34965 - Assert.assertEquals(result.getDiagnostics().length, index); + Assert.assertEquals(result.getDiagnostics().length, 0); } @Test(description = "Test type guard type narrowing with no errors") diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/matchstmt/MatchGuardAnalysisTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/matchstmt/MatchGuardAnalysisTest.java index 3b994cba5e9d..68007630242d 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/matchstmt/MatchGuardAnalysisTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/matchstmt/MatchGuardAnalysisTest.java @@ -64,14 +64,6 @@ public void testMatchGuardCodeAnalysisNegative() { BAssertUtil.validateWarning(result, i++, "pattern will not be matched", 42, 9); BAssertUtil.validateError(result, i++, "incompatible types: 'Foo' will not be " + "matched to 'record {| string x; int i; anydata...; |}'", 42, 30); - BAssertUtil.validateError(result, i++, "incompatible types: 'function () returns ()' will not be " + - "matched to 'isolated function'", 51, 19); - BAssertUtil.validateError(result, i++, "incompatible types: 'function (int,string) returns ()' " + - "will not be matched to 'isolated function'", 60, 19); - BAssertUtil.validateError(result, i++, "incompatible types: 'function (int,string) returns (int)' " + - "will not be matched to 'isolated function'", 69, 19); - BAssertUtil.validateError(result, i++, "incompatible types: 'function () returns (int)' will not be " + - "matched to 'isolated function'", 78, 19); BAssertUtil.validateError(result, i++, "incompatible types: '\"P2\"' will not be matched to '\"P3\"'", 88, 17); Assert.assertEquals(result.getWarnCount(), 3); Assert.assertEquals(result.getErrorCount(), i - 3); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ConstantExpressionTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ConstantExpressionTest.java index 14931b3531f7..e5040c89c631 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ConstantExpressionTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ConstantExpressionTest.java @@ -58,11 +58,9 @@ public void constExpressionNegative() { BAssertUtil.validateError(compileResult1, i++, "operator '~' not defined for 'false'", 37, 22); BAssertUtil.validateError(compileResult1, i++, "illegal cyclic reference '[A, B, C]'", 39, 1); BAssertUtil.validateError(compileResult1, i++, "illegal cyclic reference '[E, F]'", 44, 1); - BAssertUtil.validateError(compileResult1, i++, "'-9.223372036854776E18' is out of range for 'int'", 47, 20); BAssertUtil.validateError(compileResult1, i++, "'9223372036854775808' is out of range for 'int'", 47, 21); BAssertUtil.validateError(compileResult1, i++, "illegal cyclic reference '[CONST5]'", 49, 1); - BAssertUtil.validateError(compileResult1, i++, "cannot declare a constant with type 'T', " + - "expected a subtype of 'anydata' that is not 'never'", 49, 7); + BAssertUtil.validateError(compileResult1, i++, "constant declaration not yet supported for type 'T'", 49, 7); Assert.assertEquals(compileResult1.getErrorCount(), i); } diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ListConstantNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ListConstantNegativeTest.java index 912ce6d9130a..76a9968d7167 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ListConstantNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/ListConstantNegativeTest.java @@ -46,7 +46,6 @@ public void testListConstructorExprAsConstantExprNegative() { " does not have a filler value", 30, 41); validateError(compileResult, i++, "invalid usage of list constructor: type '1|2' does not have a filler value", 31, 26); - validateError(compileResult, i++, "ambiguous type '(int[2]|[int,int])'", 32, 38); validateError(compileResult, i++, "incompatible types: expected '1', found '3'", 33, 27); validateError(compileResult, i++, "incompatible types: expected 'byte', found '300'", 34, 24); validateError(compileResult, i++, "size mismatch in closed array. expected '2', but found '3'", 35, 23); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/SimpleConstantNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/SimpleConstantNegativeTest.java index 2a80fdcac758..ba441c2c5125 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/SimpleConstantNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/constant/SimpleConstantNegativeTest.java @@ -50,9 +50,8 @@ public void testNegative() { 9, 14); BAssertUtil.validateError(compileResult, index++, "'_' is a keyword, and may not be used as an identifier", 10, 7); - BAssertUtil.validateError(compileResult, index++, "cannot declare a constant with type 'invalidType', " + - "expected a subtype of 'anydata' that is not 'never'", - 12, 7); + BAssertUtil.validateError(compileResult, index++, "constant declaration not yet supported for type " + + "'invalidType'", 12, 7); BAssertUtil.validateError(compileResult, index++, "unknown type 'invalidType'", 12, 7); BAssertUtil.validateError(compileResult, index++, "cannot update constant value", 26, 5); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finite/FiniteTypeNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finite/FiniteTypeNegativeTest.java index 9048996d7897..d5b142770971 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finite/FiniteTypeNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finite/FiniteTypeNegativeTest.java @@ -163,7 +163,7 @@ public void testInvalidLiteralAssignment() { 183, 12); validateError(resultNegativeTwo, i++, "incompatible types: expected '2d', found 'string'", 187, 12); - validateError(resultNegativeTwo, i++, "incompatible types: 'int' cannot be cast to '2f'", + validateError(resultNegativeTwo, i++, "incompatible types: expected '2d', found '2f'", 191, 12); validateError(resultNegativeTwo, i++, "incompatible types: expected '3d', found '4f'", 192, 12); @@ -233,14 +233,14 @@ public void testInvalidLiteralAssignment() { "found 'float'", 276, 34); validateError(resultNegativeTwo, i++, "incompatible types: expected '1.7976931348623157E309d', " + "found 'decimal'", 277, 34); - validateError(resultNegativeTwo, i++, "'-9.223372036854776E18' is out of range for 'int'", - 280, 20); + validateError(resultNegativeTwo, i++, "'9223372036854775808' is out of range for 'int'", + 280, 21); validateError(resultNegativeTwo, i++, "unknown type 'testType'", 282, 5); validateError(resultNegativeTwo, i++, "'9223372036854775808' is out of range for 'int'", 285, 30); validateError(resultNegativeTwo, i++, "unknown type 'testType'", 286, 5); - validateError(resultNegativeTwo, i++, "'-9.223372036854776E18' is out of range for 'int'", - 290, 19); + validateError(resultNegativeTwo, i++, "'9223372036854775808' is out of range for 'int'", + 290, 20); validateError(resultNegativeTwo, i++, "unknown type 'InvalidTest1'", 292, 5); validateError(resultNegativeTwo, i++, "incompatible types: expected '1f', found 'float'", 296, 12); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/map/ConstrainedMapTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/map/ConstrainedMapTest.java index b216a7ee5b0e..74b48bff3127 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/map/ConstrainedMapTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/map/ConstrainedMapTest.java @@ -50,7 +50,7 @@ public void setup() { @Test(description = "Test Map constrained with type negative semantic validations.") public void testConstrainedMapNegative() { - Assert.assertEquals(negativeResult.getErrorCount(), 7); + Assert.assertEquals(negativeResult.getErrorCount(), 6); int i = 0; BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'map', found 'map'", 3, 12); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'int', found 'string'", 7, 44); @@ -61,8 +61,6 @@ public void testConstrainedMapNegative() { "incompatible types: expected 'map', " + "found 'map'", 35, 31); BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'map', found 'map'", 45, 31); - BAssertUtil.validateError(negativeResult, i, "incompatible types: 'map' cannot be cast to" + - " 'map'", 77, 29); } @Test(description = "Test Map constrained with value type value retrieval positive case.") diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/never/NeverTypeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/never/NeverTypeTest.java index e894bd8f93ac..b0d049346c0c 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/never/NeverTypeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/never/NeverTypeTest.java @@ -338,8 +338,6 @@ public Object[] dataToTestNeverRuntime() { "testNeverRuntime7", "testNeverRuntime8", "testNeverRuntime9", - "testNeverRuntime10", - "testNeverRuntime11", "testNeverRuntime12", "testNeverWithAnyAndAnydataRuntime", "testNeverFieldTypeCheck", @@ -402,6 +400,25 @@ public void testNeverTypeCodeAnalysisNegative() { Assert.assertEquals(compileResult.getWarnCount(), 1); } + @Test + public void testNeverTypeIsExprNegative() { + CompileResult res = BCompileUtil.compile("test-src/types/never/never_type_is_expr_negative.bal"); + int i = 0; + BAssertUtil.validateError(res, i++, "incompatible types: 'int' will not be matched to 'never'", 19, 17); + BAssertUtil.validateError(res, i++, "incompatible types: 'Record' will not be matched to 'never'", 29, 17); + BAssertUtil.validateError(res, i++, "incompatible types: 'record {| int x; anydata...; |}' " + + "will not be matched to 'record {| never x?; anydata...; |}'", 34, 17); + BAssertUtil.validateError(res, i++, "incompatible types: 'record {| never? x; anydata...; |}' " + + "will not be matched to 'record {| never x?; anydata...; |}'", 37, 17); + BAssertUtil.validateError(res, i++, "incompatible types: 'record {| int? x; anydata...; |}' " + + "will not be matched to 'record {| never x?; anydata...; |}'", 40, 17); + BAssertUtil.validateError(res, i++, "incompatible types: '(record {| int x; anydata...; |} & readonly)' " + + "will not be matched to 'record {| never x?; anydata...; |}'", 43, 17); + BAssertUtil.validateError(res, i++, "incompatible types: '(record {| never? x; anydata...; |} & readonly)' " + + "will not be matched to 'record {| never x?; anydata...; |}'", 46, 17); + Assert.assertEquals(res.getErrorCount(), i); + } + @AfterClass public void tearDown() { neverTypeTestResult = null; diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/InherentlyImmutableTypeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/InherentlyImmutableTypeTest.java index 58533c393875..f7f4aad79930 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/InherentlyImmutableTypeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/readonly/InherentlyImmutableTypeTest.java @@ -54,16 +54,14 @@ public void testReadonlyTypeNegative() { BAssertUtil.validateError(negativeResult, i++, "incompatible types: expected 'any', found 'readonly'", 19, 14); BAssertUtil.validateError(negativeResult, i++, - "operator '==' not defined for 'readonly' and '[int,int,int]'", 24, 14); + "incompatible types: expected 'error?', found 'readonly'", 24, 26); BAssertUtil.validateError(negativeResult, i++, - "incompatible types: expected 'error?', found 'readonly'", 29, 26); + "incompatible types: expected '(int|any)', found 'readonly'", 25, 27); BAssertUtil.validateError(negativeResult, i++, - "incompatible types: expected '(int|any)', found 'readonly'", 30, 27); + "incompatible types: expected '(string|readonly)', found '(readonly|any)'", 27, 43); BAssertUtil.validateError(negativeResult, i++, - "incompatible types: expected '(string|readonly)', found '(readonly|any)'", 32, 43); - BAssertUtil.validateError(negativeResult, i++, - "incompatible types: expected 'any', found '(readonly|string)'", 34, 17); - Assert.assertEquals(negativeResult.getErrorCount(), 6); + "incompatible types: expected 'any', found '(readonly|string)'", 29, 17); + Assert.assertEquals(negativeResult.getErrorCount(), i); } @AfterClass diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableCastTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableCastTest.java index e9c5fe210614..d478380285a7 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableCastTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableCastTest.java @@ -72,15 +72,14 @@ public void testCastingWithEmptyKeyedKeylessTbl() { @Test public void testNegativeCases() { - Assert.assertEquals(negativeResult.getErrorCount(), 5); - BAssertUtil.validateError(negativeResult, 0, "incompatible types: 'PersonTable1' " + + int index = 0; + BAssertUtil.validateError(negativeResult, index++, "incompatible types: 'PersonTable1' " + "cannot be cast to 'table key'", 49, 34); - BAssertUtil.validateError(negativeResult, 1, "incompatible types: expected 'int', found 'string'", 50, 16); - BAssertUtil.validateError(negativeResult, 2, "incompatible types: expected 'string', found 'int'", 60, 16); - BAssertUtil.validateError(negativeResult, 3, "incompatible types: 'CustomerTable' cannot be" + - " cast to 'CustomerEmptyKeyedTbl'", 77, 34); - BAssertUtil.validateError(negativeResult, 4, "incompatible types: 'CustomerTable' cannot be cast to " + - "'table'", 83, 20); + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'int', found 'string'", 50, + 16); + BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'string', found 'int'", 60, + 16); + Assert.assertEquals(negativeResult.getErrorCount(), index); } @AfterClass diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableNegativeTest.java index 90b5e63918c8..83eceba270a1 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/table/TableNegativeTest.java @@ -184,6 +184,9 @@ public void testTableNegativeCases() { validateError(compileResult, index++, "incompatible types: expected " + "'table key', " + "found 'table'", 539, 9); + validateError(compileResult, index++, "incompatible types: expected " + + "'table key', " + + "found 'table key'", 551, 9); validateError(compileResult, index++, "incompatible types: expected 'never', found 'int'", 551, 29); validateError(compileResult, index++, "incompatible types: expected " + "'table key', " + diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/BasicTupleTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/BasicTupleTest.java index 629de1bd6008..c4d4b31a3ddb 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/BasicTupleTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/tuples/BasicTupleTest.java @@ -193,7 +193,7 @@ public void testTupleWithUnion() { @Test(description = "Test negative scenarios of assigning tuple literals") public void testNegativeTupleLiteralAssignments() { - Assert.assertEquals(resultNegative.getErrorCount(), 55); + Assert.assertEquals(resultNegative.getErrorCount(), 54); int i = 0; BAssertUtil.validateError( resultNegative, i++, @@ -310,21 +310,19 @@ public void testTupleToJSONAssignmentNegative() { int i = 36; BAssertUtil.validateError(resultNegative, i++, "incompatible types: expected 'json', " + "found '[string,int,xml...]'", 199, 21); - BAssertUtil.validateError(resultNegative, i++, "incompatible types: '[string,(int|xml),string...]' " + - "cannot be cast to 'json[]'", 202, 16); BAssertUtil.validateError(resultNegative, i, "incompatible types: expected 'json', " + "found '[string,(int|xml),string...]'", 203, 16); } @Test(description = "Test ambiguous tuple assignment to unions of tuple types") public void testAmbiguousTupleTupeNegative() { - int i = 39; + int i = 38; BAssertUtil.validateError(resultNegative, i, "ambiguous type '([1,\"hello\"]|[1])'", 208, 10); } @Test(description = "Test the tuple argument when the variable is already declared") public void testTupleParamWithExistingArg() { - int i = 40; + int i = 39; BAssertUtil.validateError(resultNegative, i++, "redeclared symbol 'i'", 215, 34); BAssertUtil.validateError(resultNegative, i++, "redeclared symbol 'i'", 222, 41); BAssertUtil.validateError(resultNegative, i++, "operator '+' not defined for 'int' and 'string'", 230, 21); @@ -338,7 +336,7 @@ public void testTupleAsTupleFirstMember() { @Test(description = "Test the tuple annotations") public void testTupleAnnotations1() { - int i = 44; + int i = 43; BAssertUtil.validateError(resultNegative, i++, "annotation 'ballerina/lang.annotations:0.0.0:typeParam' is not allowed on field", 239, 7); diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLIterationTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLIterationTest.java index 2e5b28fcdbbb..fc7276975860 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLIterationTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/xml/XMLIterationTest.java @@ -58,27 +58,18 @@ public void testNegative() { BAssertUtil.validateError(negative, index++, "incompatible types: " + "expected 'function (ballerina/lang.xml:0.0.0:ItemType) returns ()', " + "found 'function ([int,xml,string]) returns ()'", 18, 19); - BAssertUtil.validateError(negative, index++, - "incompatible types: expected 'other', found 'xml:Element'", - 29, 13); BAssertUtil.validateError(negative, index++, "incompatible types: 'xml:Element' is not an iterable collection", 29, 34); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'record {| xml:Comment value; |}?', found " + "'record {| xml:Element value; |}?'", 33, 54); - BAssertUtil.validateError(negative, index++, - "incompatible types: expected 'other', found 'xml:Comment'", - 40, 13); BAssertUtil.validateError(negative, index++, "incompatible types: 'xml:Comment' is not an iterable collection", 40, 34); BAssertUtil.validateError(negative, index++, "incompatible types: expected 'record {| xml:Element value; |}?', found " + "'record {| xml:Comment value; |}?'", 44, 54); - BAssertUtil.validateError(negative, index++, - "incompatible types: expected 'other', found 'xml:ProcessingInstruction'", - 51, 13); BAssertUtil.validateError(negative, index++, "incompatible types: 'xml:ProcessingInstruction' is not an iterable collection", 51, 48); @@ -91,15 +82,9 @@ public void testNegative() { BAssertUtil.validateError(negative, index++, "incompatible types: expected '(xml|xml)', found 'xml'", 60, 44); - BAssertUtil.validateError(negative, index++, - "incompatible types: expected 'other', found '(xml:Element|xml:Text)'", - 63, 13); BAssertUtil.validateError(negative, index++, "incompatible types: '(xml:Element|xml:Text)' is not an iterable collection", 63, 44); - BAssertUtil.validateError(negative, index++, - "incompatible types: expected 'other', found '(xml:Element|xml:Text)'", - 68, 13); BAssertUtil.validateError(negative, index++, "incompatible types: '(xml|xml)' is not an iterable collection", 68, 44); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal index ae0be91693b8..bffcab3d6b6f 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation.bal @@ -153,20 +153,17 @@ function checkAnyDataEquality() { } type IntOne 1; -type FloatOne 1.0; -type IntTwo 2; -type FloatTwo 2f; function checkFiniteTypeEquality() { IntOne intOne_1 = 1; IntOne intOne_2 = 1; - IntTwo intTwo = 2; - FloatOne floatOne = 1f; - FloatTwo floatTwo = 2.0; + byte byteOne = 1; + byte byteTwo = 2; test:assertTrue((intOne_1 == intOne_2) && !(intOne_1 != intOne_2)); - test:assertTrue((floatOne != floatTwo) && !(floatOne == floatTwo)); - test:assertFalse((intOne_1 == intTwo) && !(intOne_1 != intTwo)); + test:assertTrue((intOne_1 == byteOne) && !(intOne_1 != byteOne)); + test:assertTrue(!(intOne_1 == byteTwo) && (intOne_1 != byteTwo)); + test:assertTrue(!(byteOne == byteTwo) && (byteOne != byteTwo)); } type ErrorDetail record { @@ -231,6 +228,16 @@ function testOpenRecordWithOptionalFieldsEqualityNegative() { test:assertFalse((e1 == e2) || !(e1 != e2) || (e3 == e4) || !(e3 != e4)); } +type EmployeeWithOptionalId record {| + string name; + float id?; +|}; + +type PersonWithOptionalId record {| + string name; + string id?; +|}; + function testClosedRecordWithOptionalFieldsEqualityPositive() { ClosedRecordWithOptionalFieldOne e1 = {name: "Em", one: 4000}; ClosedRecordWithOptionalFieldOne e2 = e1; @@ -239,6 +246,10 @@ function testClosedRecordWithOptionalFieldsEqualityPositive() { ClosedRecordWithOptionalFieldTwo e4 = {name: "Em"}; test:assertTrue((e1 == e2) && !(e1 != e2) && isEqual(e3, e4)); + + EmployeeWithOptionalId e5 = { name: "Maryam" }; + PersonWithOptionalId p1 = { name: "Maryam" }; + test:assertTrue(e5 == p1 && !(e5 != p1)); } function testClosedRecordWithOptionalFieldsEqualityNegative() { @@ -626,10 +637,6 @@ function checkTupleEqualityNegative() { [string, ClosedEmployee] t6 = ["hi", {name: "Em"}]; test:assertFalse(t1 == t2 || !(t1 != t2) || t3 == t4 || !(t3 != t4) || t5 == t6 || !(t5 != t6)); - - Array a = ["array", 1]; - Mapping b = ["mapping", 2]; - test:assertFalse(a == b); } function checkUnionConstrainedMapsPositive() { @@ -700,6 +707,18 @@ function checkUnionConstrainedMapsNegative() { test:assertFalse('equals || m3 == m4 || !(m3 != m4)); } +function checkEqualityOfMapsOfIncompatibleConstraintTypes() { + map a = {}; + map b = {}; + boolean bool1 = a == b && !(a != b); + + map c = {}; + map d = {}; + boolean bool2 = c == d && !(c != d); + + test:assertTrue(bool1 && bool2); +} + function checkUnionArrayPositive() { (string|int)?[] a1 = []; int[] a2 = []; @@ -1820,3 +1839,25 @@ function testEqualityWithCyclicReferences() { test:assertTrue(t3 == t4); test:assertTrue(t3 == t5); } + +function readonlyMapEquality() { + map & readonly immutableMarks = { + math: 80, + physics: 85, + chemistry: 75 + }; + readonly readonlyMarks = immutableMarks; + + map marks = { + math: 80, + physics: 85, + chemistry: 75 + }; + + test:assertTrue(readonlyMarks == marks); +} + +function readonlyListEquality() { + readonly arr = [1, 2 , 3]; + test:assertTrue(arr == [1, 2 , 3]); +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation_negative.bal index d6b45203a8c1..c9fbed15a20c 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/equal_and_not_equal_operation_negative.bal @@ -32,18 +32,6 @@ function checkEqualityOfArraysOfDifferentTypes() returns boolean { return bool1 && bool2; } -function checkEqualityOfMapsOfIncompatibleConstraintTypes() returns boolean { - map a = {}; - map b = {}; - boolean bool1 = a == b && !(a != b); - - map c = {}; - map d = {}; - boolean bool2 = c == d && !(c != d); - - return bool1 && bool2; -} - function checkEqualityOfTuplesOfDifferentTypes() returns boolean { [string, int] a = ["", 0]; [boolean, float] b = [false, 0.0]; @@ -60,10 +48,6 @@ function checkEqualityOfRecordsOfIncompatibleTypes() returns boolean { Employee e = { name: "Maryam" }; Person p = { name: "Maryam" }; boolean b = e == p && !(e != p); - - EmployeeWithOptionalId e1 = { name: "Maryam" }; - PersonWithOptionalId p1 = { name: "Maryam" }; - return b && e1 == p1 && !(e1 != p1); } function checkEqualityWithJsonRecordMapForIncompatibleType() returns boolean { @@ -123,11 +107,6 @@ type EmployeeWithOptionalId record {| float id?; |}; -type PersonWithOptionalId record {| - string name; - string id?; -|}; - class Foo { string s = ""; } @@ -152,22 +131,6 @@ function refAndNilEqualityCheck() { } } -function readonlyEquality() returns boolean { - map & readonly immutableMarks = { - math: 80, - physics: 85, - chemistry: 75 - }; - readonly readonlyMarks = immutableMarks; - - map marks = { - math: 80, - physics: 85, - chemistry: 75 - }; - return readonlyMarks != marks; -} - type MyObject object { int i; }; @@ -183,3 +146,27 @@ function testEqualityWithNonAnydataType() returns boolean { MyObject obj2 = object {int i = 10;}; 'equals = obj == obj2 && !(obj != obj2); } + +type IntOne 1; +type FloatOne 1.0; +type IntTwo 2; +type FloatTwo 2f; + +function checkFiniteTypeEqualityNegative() { + IntOne intOne_1 = 1; + IntTwo intTwo = 2; + FloatOne floatOne = 1f; + FloatTwo floatTwo = 2.0; + + boolean _ = (floatOne != floatTwo) && !(floatOne == floatTwo); + boolean _ = ((intOne_1 == intTwo) && !(intOne_1 != intTwo)); +} + +type Array ["array", 1]; +type Mapping ["mapping", 2]; + +function checkTupleEqualityNegative() { + Array a = ["array", 1]; + Mapping b = ["mapping", 2]; + boolean _ = a == b; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/negative-type-test-expr.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/negative-type-test-expr.bal index b06341511c3e..9565ccb4161c 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/negative-type-test-expr.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/negative-type-test-expr.bal @@ -92,19 +92,19 @@ function testTypeCheckInTernary() returns string { // ========================== Records ========================== -type A1 record { +type A1NTT record { int x = 0; }; -type B1 record { +type B1NTT record { int x = 0; string y = ""; }; function testSimpleRecordTypes_1() returns string { - A1 a1 = {}; + A1NTT a1 = {}; any a = a1; - if (a !is A1) { + if (a !is A1NTT) { return "n/a"; } else { return "a is A1"; @@ -112,49 +112,49 @@ function testSimpleRecordTypes_1() returns string { } function testSimpleRecordTypes_2() returns [boolean, boolean] { - B1 b = {}; + B1NTT b = {}; any a = b; - return [a !is A1, a !is B1]; + return [a !is A1NTT, a !is B1NTT]; } -type A2 record { +type A2NTT record { int x = 0; }; -type B2 record { +type B2NTT record { int x = 0; }; function testSimpleRecordTypes_3() returns [boolean, boolean] { - B2 b = {}; + B2NTT b = {}; any a = b; - return [a !is A2, a !is B2]; + return [a !is A2NTT, a !is B2NTT]; } -type Human record { +type HumanNTT record { string name; (function (int, string) returns string) | () foo = (); }; -type Man record { +type ManNTT record { string name; (function (int, string) returns string) | () foo = (); int age = 0; }; function testRecordsWithFunctionType_1() returns [string, string] { - Human m = {name:"Piyal"}; + HumanNTT m = {name:"Piyal"}; any a = m; string s1; string s2; - if (a !is Man) { + if (a !is ManNTT) { s1 = "a is not a man"; } else { s1 = "Man: " + m.name; } - if (a !is Human) { + if (a !is HumanNTT) { s2 = "a is not a human"; } else { s2 = "Human: " + m.name; @@ -164,18 +164,18 @@ function testRecordsWithFunctionType_1() returns [string, string] { } function testRecordsWithFunctionType_2() returns [string, string] { - Man m = {name:"Piyal"}; + ManNTT m = {name:"Piyal"}; any a = m; string s1; string s2; - if (a !is Man) { + if (a !is ManNTT) { s1 = "a is not a man"; } else { s1 = "Man: " + m.name; } - if (a !is Human) { + if (a !is HumanNTT) { s2 = "a is not a human"; } else { s2 = "Human: " + m.name; @@ -184,36 +184,36 @@ function testRecordsWithFunctionType_2() returns [string, string] { return [s1, s2]; } -type X record { +type XTN record { int p = 0; string q = ""; - A1 r = {}; + A1NTT r = {}; }; -type Y record { +type YTN record { int p = 0; string q = ""; - B1 r = {}; // Assignable to A1. Hence Y is assignable to X. + B1NTT r = {}; // Assignable to A1. Hence Y is assignable to X. }; function testNestedRecordTypes() returns [boolean, boolean] { - Y y = {}; + YTN y = {}; any x = y; - return [x is X, x is Y]; + return [x is XTN, x is YTN]; } -type A3 record { +type A3NTT record { int x = 0; }; -type B3 record {| +type B3NTT record {| int x = 0; |}; function testSealedRecordTypes() returns [boolean, boolean] { - A3 a3 = {}; + A3NTT a3 = {}; any a = a3; - return [a !is A3, a !is B3]; + return [a !is A3NTT, a !is B3NTT]; } // ========================== Objects ========================== @@ -375,18 +375,18 @@ function testObjectWithUnorderedFields() returns [string, string, string, string return [s1, s2, s3, s4]; } -public type A4 object { +public type A4NTT object { public int p; public string q; }; -public type B4 object { +public type B4NTT object { public float r; - *A4; + *A4NTT; }; public class C4 { - *B4; + *B4NTT; public boolean s; public function init(int p, string q, float r, boolean s) { @@ -403,11 +403,11 @@ function testPublicObjectEquivalency() returns [string, string, string] { string s2 = "n/a"; string s3 = "n/a"; - if !(x !is A4) { + if !(x !is A4NTT) { s1 = "values: " + x.p.toString() + ", " + x.q; } - if !(x !is B4) { + if !(x !is B4NTT) { s2 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -418,18 +418,18 @@ function testPublicObjectEquivalency() returns [string, string, string] { return [s1, s2, s3]; } -type A5 object { +type A5NTT object { int p; string q; }; -type B5 object { +type B5NTT object { float r; - *A5; + *A5NTT; }; class C5 { - *B5; + *B5NTT; boolean s; public function init(int p, string q, float r, boolean s) { @@ -446,11 +446,11 @@ function testPrivateObjectEquivalency() returns [string, string, string] { string s2 = "n/a"; string s3 = "n/a"; - if !(x !is A5) { + if !(x !is A5NTT) { s1 = "values: " + x.p.toString() + ", " + x.q; } - if !(x !is B5) { + if !(x !is B5NTT) { s2 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -467,7 +467,7 @@ function testAnonymousObjectEquivalency() returns [string, string, string] { string s2 = "n/a"; string s3 = "n/a"; - if !(x !is object { public float r; *A4; }) { + if !(x !is object { public float r; *A4NTT; }) { s1 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -482,50 +482,50 @@ function testAnonymousObjectEquivalency() returns [string, string, string] { return [s1, s2, s3]; } -class Qux { - Qux? fn; +class QuxNeg { + QuxNeg? fn; - public function init(Qux? fn = ()) { + public function init(QuxNeg? fn = ()) { self.fn = fn; } } -class Quux { - Quux? fn = (); +class QuuxNeg { + QuuxNeg? fn = (); } -class Quuz { - Quuz? fn = (); +class QuuzNeg { + QuuzNeg? fn = (); int i = 1; } -class ABC { - Qux f; +class ABCNeg { + QuxNeg f; string s; - function init(Qux f, string s) { + function init(QuxNeg f, string s) { self.f = f; self.s = s; } } function testObjectIsCheckWithCycles() { - Qux f1 = new; - Qux f2 = new (f1); + QuxNeg f1 = new; + QuxNeg f2 = new (f1); - any a1 = f1; - assertFalse(a1 !is Quux); - assertTrue(a1 !is Quuz); + any a1 = f1; + assertFalse(a1 !is QuuxNeg); + assertTrue(a1 !is QuuzNeg); - any a2 = f2; - assertFalse(a2 !is Quux); - assertTrue(a2 !is Quuz); + any a2 = f2; + assertFalse(a2 !is QuuxNeg); + assertTrue(a2 !is QuuzNeg); - ABC ob = new (f2, "ballerina"); + ABCNeg ob = new (f2, "ballerina"); any a3 = ob; - assertFalse(a3 !is object { Qux f; }); - assertTrue(a3 !is object { Quuz f; }); + assertFalse(a3 !is object {QuxNeg f;}); + assertTrue(a3 !is object {QuuzNeg f;}); } // ========================== Arrays ========================== @@ -539,11 +539,11 @@ function testSimpleArrays() returns [boolean, boolean, boolean, boolean, boolean } function testRecordArrays() returns [boolean, boolean, boolean, boolean] { - X[] a = [{}, {}]; - X[][] b = [[{}, {}], [{}, {}]]; + XTN[] a = [{}, {}]; + XTN[][] b = [[{}, {}], [{}, {}]]; any c = a; any d = b; - return [c !is X[], d !is X[][], c !is Y[], d !is Y[][]]; + return [c !is XTN[], d !is XTN[][], c !is YTN[], d !is YTN[][]]; } public function testUnionType() { @@ -641,20 +641,20 @@ function testSimpleTuples() returns [boolean, boolean, boolean, boolean, boolean } function testTupleWithAssignableTypes_1() returns [boolean, boolean, boolean, boolean] { - [X, Y] p = [{}, {}]; + [XTN, YTN] p = [{}, {}]; any q = p; - boolean b0 = q !is [X, X]; - boolean b1 = q !is [X, Y]; - boolean b2 = q !is [Y, X]; - boolean b3 = q !is [Y, Y]; + boolean b0 = q !is [XTN, XTN]; + boolean b1 = q !is [XTN, YTN]; + boolean b2 = q !is [YTN, XTN]; + boolean b3 = q !is [YTN, YTN]; return [b0, b1, b2, b3]; } function testTupleWithAssignableTypes_2() returns boolean { - [Y, Y] p = [{}, {}]; - [X, Y] q = p; - boolean b1 = q !is [Y, Y]; - return q !is [Y, Y]; + [YTN, YTN] p = [{}, {}]; + [XTN, YTN] q = p; + boolean b1 = q !is [YTN, YTN]; + return q !is [YTN, YTN]; } public function testRestType() { @@ -1079,12 +1079,12 @@ public function testXMLNeverType() { xml e = xml ``; assertEquality( e !is byte, true); - assertEquality( e !is xml<'xml:Element>, true); + assertEquality( e is xml<'xml:Element>, true); assertEquality( e !is xml<'xml:Text>, false); assertEquality( e !is xml, false); assertEquality( e !is 'xml:Text, false); assertEquality( e !is 'xml:Element, true); - assertEquality( e !is xml<'xml:Element|'xml:Comment>, true); + assertEquality( e is xml<'xml:Element|'xml:Comment>, true); } function testXMLTextType(){ diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/type-test-expr.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/type-test-expr.bal index b6c8c183f2ba..3a8c6533ec95 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/type-test-expr.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/binaryoperations/type-test-expr.bal @@ -100,21 +100,21 @@ function testTypeCheckInTernary() returns string { // ========================== Records ========================== -type A1 record { +type A1TN record { int x = 0; }; -type B1 record { +type B1TN record { int x = 0; string y = ""; }; function testSimpleRecordTypes_1() returns string { - A1 a1 = {}; + A1TN a1 = {}; any a = a1; - if (a is A1) { + if (a is A1TN) { return "a is A1"; - } else if (a is B1) { + } else if (a is B1TN) { return "a is B1"; } @@ -122,49 +122,49 @@ function testSimpleRecordTypes_1() returns string { } function testSimpleRecordTypes_2() returns [boolean, boolean] { - B1 b = {}; + B1TN b = {}; any a = b; - return [a is A1, a is B1]; + return [a is A1TN, a is B1TN]; } -type A2 record { +type A2TN record { int x = 0; }; -type B2 record { +type B2TN record { int x = 0; }; function testSimpleRecordTypes_3() returns [boolean, boolean] { - B2 b = {}; + B2TN b = {}; any a = b; - return [a is A2, a is B2]; + return [a is A2TN, a is B2TN]; } -type Human record { +type HumanTN record { string name; (function (int, string) returns string) | () foo = (); }; -type Man record { +type ManTN record { string name; (function (int, string) returns string) | () foo = (); int age = 0; }; function testRecordsWithFunctionType_1() returns [string, string] { - Human m = {name:"Piyal"}; + HumanTN m = {name:"Piyal"}; any a = m; string s1; string s2; - if (a is Man) { + if (a is ManTN) { s1 = "Man: " + m.name; } else { s1 = "a is not a man"; } - if (a is Human) { + if (a is HumanTN) { s2 = "Human: " + m.name; } else { s2 = "a is not a human"; @@ -174,18 +174,18 @@ function testRecordsWithFunctionType_1() returns [string, string] { } function testRecordsWithFunctionType_2() returns [string, string] { - Man m = {name:"Piyal"}; + ManTN m = {name:"Piyal"}; any a = m; string s1; string s2; - if (a is Man) { + if (a is ManTN) { s1 = "Man: " + m.name; } else { s1 = "a is not a man"; } - if (a is Human) { + if (a is HumanTN) { s2 = "Human: " + m.name; } else { s2 = "a is not a human"; @@ -194,39 +194,39 @@ function testRecordsWithFunctionType_2() returns [string, string] { return [s1, s2]; } -type X record { +type XTTE record { int p = 0; string q = ""; - A1 r = {}; + A1TN r = {}; }; -type Y record { +type YTTE record { int p = 0; string q = ""; - B1 r = {}; // Assignable to A1. Hence Y is assignable to X. + B1TN r = {}; // Assignable to A1. Hence Y is assignable to X. }; function testNestedRecordTypes() returns [boolean, boolean] { - Y y = {}; + YTTE y = {}; any x = y; - return [x is X, x is Y]; + return [x is XTTE, x is YTTE]; } -type A3 record { +type A3TN record { int x = 0; }; -type B3 record {| +type B3TN record {| int x = 0; |}; function testSealedRecordTypes() returns [boolean, boolean] { - A3 a3 = {}; + A3TN a3 = {}; any a = a3; - return [a is A3, a is B3]; + return [a is A3TN, a is B3TN]; } -type Country record {| +type CountryTN record {| readonly string code?; string name?; record {| @@ -236,7 +236,7 @@ type Country record {| |} continent?; |}; -type MyCountry record {| +type MyCountryTN record {| readonly string code?; record {| string code?; @@ -245,9 +245,9 @@ type MyCountry record {| |}; function testRecordsWithOptionalFields() { - MyCountry x = {}; - Country y = x; - test:assertTrue(x is Country); + MyCountryTN x = {}; + CountryTN y = x; + test:assertTrue(x is CountryTN); } // ========================== Objects ========================== @@ -409,18 +409,18 @@ function testObjectWithUnorderedFields() returns [string, string, string, string return [s1, s2, s3, s4]; } -public type A4 object { +public type A4TN object { public int p; public string q; }; -public type B4 object { +public type B4TN object { public float r; - *A4; + *A4TN; }; -public class C4 { - *B4; +public class C4TN { + *B4TN; public boolean s; public function init(int p, string q, float r, boolean s) { @@ -432,16 +432,16 @@ public class C4 { } function testPublicObjectEquivalency() returns [string, string, string] { - any x = new C4(5, "foo", 6.7, true); + any x = new C4TN(5, "foo", 6.7, true); string s1 = "n/a"; string s2 = "n/a"; string s3 = "n/a"; - if(x is A4) { + if(x is A4TN) { s1 = "values: " + x.p.toString() + ", " + x.q; } - if (x is B4) { + if (x is B4TN) { s2 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -452,18 +452,18 @@ function testPublicObjectEquivalency() returns [string, string, string] { return [s1, s2, s3]; } -type A5 object { +type A5TN object { int p; string q; }; -type B5 object { +type B5TN object { float r; - *A5; + *A5TN; }; class C5 { - *B5; + *B5TN; boolean s; public function init(int p, string q, float r, boolean s) { @@ -480,11 +480,11 @@ function testPrivateObjectEquivalency() returns [string, string, string] { string s2 = "n/a"; string s3 = "n/a"; - if(x is A5) { + if(x is A5TN) { s1 = "values: " + x.p.toString() + ", " + x.q; } - if (x is B5) { + if (x is B5TN) { s2 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -496,12 +496,12 @@ function testPrivateObjectEquivalency() returns [string, string, string] { } function testAnonymousObjectEquivalency() returns [string, string, string] { - any x = new C4(5, "foo", 6.7, true); + any x = new C4TN(5, "foo", 6.7, true); string s1 = "n/a"; string s2 = "n/a"; string s3 = "n/a"; - if(x is object { public float r; *A4; }) { + if(x is object { public float r; *A4TN; }) { s1 = "values: " + x.p.toString() + ", " + x.q + ", " + x.r.toString(); } @@ -516,50 +516,50 @@ function testAnonymousObjectEquivalency() returns [string, string, string] { return [s1, s2, s3]; } -class Qux { - Qux? fn; +class QuxFoo { + QuxFoo? fn; - public function init(Qux? fn = ()) { + public function init(QuxFoo? fn = ()) { self.fn = fn; } } -class Quux { - Quux? fn = (); +class QuuxFoo { + QuuxFoo? fn = (); } -class Quuz { - Quuz? fn = (); +class QuuzFoo { + QuuzFoo? fn = (); int i = 1; } -class ABC { - Qux f; +class ABCFoo { + QuxFoo f; string s; - function init(Qux f, string s) { + function init(QuxFoo f, string s) { self.f = f; self.s = s; } } function testObjectIsCheckWithCycles() { - Qux f1 = new; - Qux f2 = new (f1); + QuxFoo f1 = new; + QuxFoo f2 = new (f1); - any a1 = f1; - test:assertTrue(a1 is Quux); - test:assertFalse(a1 is Quuz); + any a1 = f1; + test:assertTrue(a1 is QuuxFoo); + test:assertFalse(a1 is QuuzFoo); - any a2 = f2; - test:assertTrue(a2 is Quux); - test:assertFalse(a2 is Quuz); + any a2 = f2; + test:assertTrue(a2 is QuuxFoo); + test:assertFalse(a2 is QuuzFoo); - ABC ob = new (f2, "ballerina"); + ABCFoo ob = new (f2, "ballerina"); any a3 = ob; - test:assertTrue(a3 is object { Qux f; }); - test:assertFalse(a3 is object { Quuz f; }); + test:assertTrue(a3 is object {QuxFoo f;}); + test:assertFalse(a3 is object {QuuzFoo f;}); } service class ServiceClassA { @@ -625,11 +625,11 @@ function testSimpleArrays() returns [boolean, boolean, boolean, boolean, boolean } function testRecordArrays() returns [boolean, boolean, boolean, boolean] { - X[] a = [{}, {}]; - X[][] b = [[{}, {}], [{}, {}]]; + XTTE[] a = [{}, {}]; + XTTE[][] b = [[{}, {}], [{}, {}]]; any c = a; any d = b; - return [c is X[], d is X[][], c is Y[], d is Y[][]]; + return [c is XTTE[], d is XTTE[][], c is YTTE[], d is YTTE[][]]; } public function testUnionType() { @@ -742,20 +742,20 @@ function testSimpleTuples() returns [boolean, boolean, boolean, boolean, boolean } function testTupleWithAssignableTypes_1() returns [boolean, boolean, boolean, boolean] { - [X, Y] p = [{}, {}]; + [XTTE, YTTE] p = [{}, {}]; any q = p; - boolean b0 = q is [X, X]; - boolean b1 = q is [X, Y]; - boolean b2 = q is [Y, X]; - boolean b3 = q is [Y, Y]; + boolean b0 = q is [XTTE, XTTE]; + boolean b1 = q is [XTTE, YTTE]; + boolean b2 = q is [YTTE, XTTE]; + boolean b3 = q is [YTTE, YTTE]; return [b0, b1, b2, b3]; } function testTupleWithAssignableTypes_2() returns boolean { - [Y, Y] p = [{}, {}]; - [X, Y] q = p; - boolean b1 = q is [Y, Y]; - return q is [Y, Y]; + [YTTE, YTTE] p = [{}, {}]; + [XTTE, YTTE] q = p; + boolean b1 = q is [YTTE, YTTE]; + return q is [YTTE, YTTE]; } public function testRestType() { @@ -852,35 +852,35 @@ function testJsonArrays() returns [boolean, boolean, boolean] { // ========================== Finite type ========================== -type State "on"|"off"; +type StateTN "on"|"off"; function testFiniteType() returns [boolean, boolean, boolean] { - State a = "on"; + StateTN a = "on"; any b = a; any c = "off"; any d = "hello"; - return [b is State, c is State, d is State]; + return [b is StateTN, c is StateTN, d is StateTN]; } function testFiniteTypeInTuple() returns [boolean, boolean, boolean, boolean] { - [State, string] x = ["on", "off"]; + [StateTN, string] x = ["on", "off"]; any y = x; - boolean b0 = y is [State, State]; - boolean b1 = y is [State, string]; - boolean b2 = y is [string, State]; + boolean b0 = y is [StateTN, StateTN]; + boolean b1 = y is [StateTN, string]; + boolean b2 = y is [string, StateTN]; boolean b3 = y is [string, string]; return [b0, b1, b2, b3]; } -function testFiniteTypeInTuplePoisoning() returns [State, State] { - [State, string] x = ["on", "off"]; +function testFiniteTypeInTuplePoisoning() returns [StateTN, StateTN] { + [StateTN, string] x = ["on", "off"]; any y = x; - [State, State] z = ["on", "on"]; + [StateTN, StateTN] z = ["on", "on"]; - if (y is [State, State]) { + if (y is [StateTN, StateTN]) { z = y; } @@ -892,11 +892,11 @@ public const APPLE = "apple"; public const ORANGE = "orange"; public const GRAPE = "grape"; -type Fruit APPLE | ORANGE | GRAPE; +type FruitTN APPLE | ORANGE | GRAPE; function testFiniteType_1() returns string { any a = APPLE; - if (a is Fruit) { + if (a is FruitTN) { return "a is a fruit"; } @@ -1001,13 +1001,13 @@ function testIntersectingUnionFalse() returns [boolean, boolean] { function testValueTypeAsFiniteTypeTrue() returns [boolean, boolean] { string s = "orange"; float f = 2.0; - return [s is Fruit, f is IntTwo]; + return [s is FruitTN, f is IntTwo]; } function testValueTypeAsFiniteTypeFalse() returns [boolean, boolean] { string s = "mango"; float f = 12.0; - return [s is Fruit, f is IntTwo]; + return [s is FruitTN, f is IntTwo]; } const ERR_REASON = "error reason"; @@ -1213,12 +1213,12 @@ public function testXMLNeverType() { xml e = xml ``; test:assertEquals( e is byte, false); - test:assertEquals( e is xml<'xml:Element>, false); + test:assertEquals( e is xml<'xml:Element>, true); test:assertEquals( e is xml<'xml:Text>, true); test:assertEquals( e is xml, true); test:assertEquals( e is 'xml:Text, true); test:assertEquals( e is 'xml:Element, false); - test:assertEquals( e is xml<'xml:Element|'xml:Comment>, false); + test:assertEquals( e is xml<'xml:Element|'xml:Comment>, true); } function testXMLTextType(){ @@ -1428,7 +1428,7 @@ type MyClientObjectType client object { }; function testResourceMethodTyping() { - client object {} objectVar = client object { + object {} objectVar = client object { resource function post .() { } }; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/listconstructor/list_constructor_infer_type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/listconstructor/list_constructor_infer_type.bal index 8a4be1b310b8..2001b8768bc0 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/listconstructor/list_constructor_infer_type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/listconstructor/list_constructor_infer_type.bal @@ -14,12 +14,12 @@ // specific language governing permissions and limitations // under the License. -type Foo record { +type FooListInfer record { string s; int i = 1; }; -class Bar { +class BarListInfer { int i; public function init(int i) { @@ -27,8 +27,8 @@ class Bar { } } -Foo f = {s: "hello"}; -Bar b = new (1); +FooListInfer f = {s: "hello"}; +BarListInfer b = new (1); json j = 1; function inferSimpleTuple() { @@ -40,14 +40,14 @@ function inferSimpleTuple() { function inferStructuredTuple() { var x = [f, b, xml `text`, j]; typedesc ta = typeof x; - assertEquality("typedesc [Foo,Bar,lang.xml:Text,json]", ta.toString()); + assertEquality("typedesc [FooListInfer,BarListInfer,lang.xml:Text,json]", ta.toString()); } function inferNestedTuple() { int[2] arr = [1, 2]; var x = [1, 2.0d, [3, f, [b, b]], arr, j]; typedesc ta = typeof x; - assertEquality("typedesc [int,decimal,[int,Foo,[Bar,Bar]],int[2],json]", ta.toString()); + assertEquality("typedesc [int,decimal,[int,FooListInfer,[BarListInfer,BarListInfer]],int[2],json]", ta.toString()); } function testInferSameRecordsInTuple() { @@ -102,15 +102,15 @@ function testInferringForReadOnly() { error err = res; assertEquality("modification not allowed on readonly value", err.detail()["message"]); - Foo & readonly foo = { + FooListInfer & readonly foo = { s: "May", i: 20 }; readonly rd2 = [1, [b, false], foo, foo]; - assertEquality(true, rd2 is [int, [boolean, boolean], Foo, Foo & readonly] & readonly); - assertEquality(false, rd2 is [int, [boolean, boolean], object {} & readonly, Foo & readonly] & readonly); - [int, [boolean, boolean], Foo, Foo] arr2 = <[int, [boolean, boolean], Foo, Foo] & readonly> checkpanic rd2; + assertEquality(true, rd2 is [int, [boolean, boolean], FooListInfer, FooListInfer & readonly] & readonly); + assertEquality(false, rd2 is [int, [boolean, boolean], object {} & readonly, FooListInfer & readonly] & readonly); + [int, [boolean, boolean], FooListInfer, FooListInfer] arr2 = <[int, [boolean, boolean], FooListInfer, FooListInfer] & readonly> checkpanic rd2; fn = function() { arr2[0] = 2; @@ -123,17 +123,17 @@ function testInferringForReadOnly() { } function testInferringForReadOnlyInUnion() { - Foo & readonly foo = { + FooListInfer & readonly foo = { s: "May", i: 20 }; boolean b = true; - readonly|(Foo|int)[] rd = [1, [b, false], foo, foo]; + readonly|(FooListInfer|int)[] rd = [1, [b, false], foo, foo]; - assertEquality(true, rd is [int, [boolean, boolean], Foo, Foo & readonly] & readonly); - assertEquality(false, rd is [int, [boolean, boolean], object {} & readonly, Foo & readonly] & readonly); - [int, [boolean, boolean], Foo, Foo] arr = <[int, [boolean, boolean], Foo, Foo] & readonly> checkpanic rd; + assertEquality(true, rd is [int, [boolean, boolean], FooListInfer, FooListInfer & readonly] & readonly); + assertEquality(false, rd is [int, [boolean, boolean], object {} & readonly, FooListInfer & readonly] & readonly); + [int, [boolean, boolean], FooListInfer, FooListInfer] arr = <[int, [boolean, boolean], FooListInfer, FooListInfer] & readonly> checkpanic rd; var fn = function() { arr[0] = 2; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type-casting.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type-casting.bal index cb1f503e661f..eb260a6c4ec9 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type-casting.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type-casting.bal @@ -481,27 +481,27 @@ function testStringToJson(string s) returns (json) { return s; } -type Person record { +type PersonTC record { string name; int age; map address = {}; int[] marks = []; - Person | () parent = (); + PersonTC | () parent = (); json info = {}; anydata a = 0; float score = 0.0; boolean alive = true; }; -type Student record { +type StudentTC record { string name; int age; map address = {}; int[] marks = []; }; -function testStructToStruct() returns (Student) { - Person p = { name:"Supun", +function testStructToStruct() returns (StudentTC) { + PersonTC p = { name:"Supun", age:25, parent:{name:"Parent", age:50}, address:{"city":"Kandy", "country":"SriLanka"}, @@ -512,13 +512,13 @@ function testStructToStruct() returns (Student) { return p2; } -function testNullStructToStruct() returns Student { - Person? p = (); - return p; +function testNullStructToStruct() returns StudentTC { + PersonTC? p = (); + return p; } -function testStructAsAnyToStruct() returns Person|error { - Person p1 = { name:"Supun", +function testStructAsAnyToStruct() returns PersonTC|error { + PersonTC p1 = { name:"Supun", age:25, parent:{name:"Parent", age:50}, address:{"city":"Kandy", "country":"SriLanka"}, @@ -526,11 +526,11 @@ function testStructAsAnyToStruct() returns Person|error { marks:[24, 81] }; any a = p1; - var p2 = check trap a; + var p2 = check trap a; return p2; } -function testAnyToStruct() returns Person { +function testAnyToStruct() returns PersonTC { json address = {"city":"Kandy", "country":"SriLanka"}; map parent = {name:"Parent", age:50}; map info = {status:"single"}; @@ -543,18 +543,18 @@ function testAnyToStruct() returns Person { marks:marks }; any b = a; - var p2 = b; + var p2 = b; return p2; } -function testAnyNullToStruct() returns Person { +function testAnyNullToStruct() returns PersonTC { any a = (); - var p = a; + var p = a; return p; } function testRecordToAny() returns (any) { - Person p = { name:"Supun", + PersonTC p = { name:"Supun", age:25, parent:{name:"Parent", age:50}, address:{"city":"Kandy", "country":"SriLanka"}, @@ -601,7 +601,7 @@ function testBooleanInJsonToInt() { } } -type Address record { +type AddressTC record { string city; string country = ""; }; @@ -671,7 +671,7 @@ function testAnyMapToJson() returns json { } function testAnyStructToJson() returns json { - Address adrs = {city:"CA"}; + AddressTC adrs = {city:"CA"}; any a = adrs; json value; value = a; @@ -917,18 +917,18 @@ function testJSONValueCasting() returns [string|error, int|error, float|error, b } function testAnyToTable() { - table tb = table [ + table tb = table [ {id:1, name:"Jane"}, {id:2, name:"Anne"} ]; any anyValue = tb; - var casted = > anyValue; - table castedValue = casted; + var casted = > anyValue; + table castedValue = casted; assertEquality("[{\"id\":1,\"name\":\"Jane\"},{\"id\":2,\"name\":\"Anne\"}]", castedValue.toString()); } -type Employee record { +type EmployeeTC record { int id; string name; }; @@ -998,32 +998,31 @@ function testCastOfReadonlyUnionArrayToByteArray() { assertEquality("[1,2,3]", f.toString()); } -type Foo record {| +type FooTC record {| string s; int[] arr; |}; -type Bar record {| +type BarTC record {| string s; byte[] arr; |}; function testCastOfReadonlyRecord() { - (Foo & readonly) f = {s: "a", arr: [1,2,3]}; + (FooTC & readonly) f = {s: "a", arr: [1,2,3]}; any a = f; - Bar b = a; + BarTC b = a; assertEquality(true, b === a); assertEquality("{\"s\":\"a\",\"arr\":[1,2,3]}", b.toString()); } function testCastOfReadonlyRecordNegative() { - (Foo & readonly) f = {s: "a", arr: [1,2,300]}; + (FooTC & readonly) f = {s: "a", arr: [1,2,300]}; any a = f; - Bar|error b = trap a; + BarTC|error b = trap a; assertEquality(true, b is error); error err = b; - string errMsg = "incompatible types: '(Foo & readonly)' cannot be cast to 'Bar': " + - "\n\t\tfield 'arr' in record 'Bar' should be of type 'byte[]', found '[1,2,300]'"; + string errMsg = "incompatible types: '(FooTC & readonly)' cannot be cast to 'BarTC'"; assertEquality("{ballerina}TypeCastError", err.message()); assertEquality(errMsg, checkpanic err.detail()["message"]); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type_cast_expr_runtime_errors.bal b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type_cast_expr_runtime_errors.bal new file mode 100644 index 000000000000..677ca81a319f --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/expressions/typecast/type_cast_expr_runtime_errors.bal @@ -0,0 +1,245 @@ + // Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). + // + // WSO2 LLC. licenses this file to you under the Apache License, + // Version 2.0 (the "License"); you may not use this file except + // in compliance with the License. + // You may obtain a copy of the License at + // + // http://www.apache.org/licenses/LICENSE-2.0 + // + // Unless required by applicable law or agreed to in writing, + // software distributed under the License is distributed on an + // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + // KIND, either express or implied. See the License for the + // specific language governing permissions and limitations + // under the License. + +function f1() { + xml A = xml `xml string`; + [string, int|xml, string...] C = ["text1", 1, A.toString()]; + json jsonTest = C; +} + +function testTupleToJSONCastRuntimeError() returns error? { + var e = trap f1(); + return e is error ? e : (); +} + +type Teacher record { + readonly string name; + readonly int age; + string school; +}; + +type Customer record { + readonly int id; + readonly string name; + string lname; +}; + +type CustomerTable table; + +CustomerTable tab3 = table key(name) [ + {id: 13, name: "Foo", lname: "QWER"}, + {id: 13, name: "Bar", lname: "UYOR"} +]; + +type Customer2 record {| + int id; + string name; + string lname; +|}; + +type CustomerEmptyKeyedTbl table key(); + +function f2() { + CustomerEmptyKeyedTbl tbl1 = tab3; +} + +function testCastingWithEmptyKeyedKeylessTbl() returns error? { + var e = trap f2(); + return e is error ? e : (); +} + +type Student record { + int index; + int age; +}; + +type Person record { + string name; + int age; + string address; +}; + +function f3() returns map { + map testPMap = {}; + map testSMap = >testPMap; + return testSMap; +} + +function testMapCastingRuntimeError() returns error? { + var e = trap f3(); + return e is error ? e : (); +} + +function f4() { + string[]|int val1 = []; + byte[] a = val1; +} + +function testListCastingRuntimeError() returns error? { + var e = trap f4(); + return e is error ? e : (); +} + +public class person01 { + + public int age = 0; + public string name = ""; + public string address = ""; + +} + +public class employee01 { + + public int age = 0; + public string name = ""; + public string zipcode = "95134"; + + function init (int age, string name) { + self.age = age; + self.name = name; + } +} + +function f5() returns string { + employee01 e = new (14, "rat"); + person01 p = e; + return p.name; +} + +function testCastingObjects() returns error? { + var e = trap f5(); + return e is error ? e : (); +} + +public class employee08 { + + public int age = 0; + public string name = ""; + public string address = ""; + public string zipcode = "95134"; + public string ssn = ""; + + + function init (int age, string name) { + self.age = age; + self.name = name; + } + + public function getName() returns string { + return self.name; + } + + public function getAge() returns int { + return self.age; + } + + public function getSSN() returns string { + return self.ssn; + } +} + +public class person08 { + + public int age = 0; + public string name = ""; + public string address = ""; + public string zipcode = "95134"; + public string ssn = ""; + + + public function getAge() returns int { + return self.age; + } + + public function getName() returns string { + return self.name; + } + + public function setSSN(string s) { + self.ssn = s; + } +} + +function f6() returns string { + employee08 e = new (14, "rat"); + person08 p = e; + return p.name; +} + +function testCastingObjects2() returns error? { + var e = trap f6(); + return e is error ? e : (); +} + +public class person09 { + + public int age = 0; + public string name = ""; + public string address = ""; + public string zipcode = "95134"; + public string ssn = ""; + + + public function getAge() returns int { + return self.age; + } + + public function getName() returns string { + return self.name; + } + + public function setSSN(string s) { + self.ssn = s; + } +} + +public class employee09 { + + public int age = 0; + public string name = ""; + public string address = ""; + public string zipcode = "95134"; + public string ssn = ""; + + + function init (int age, string name) { + self.age = age; + self.name = name; + } + + public function getName() returns string { + return self.name; + } + + public function getAge(int i) returns int { + return self.age; + } + + public function getSSN() returns string { + return self.ssn; + } +} + +function f7() returns string { + employee09 e = new (14, "rat"); + person09 p = e; + return p.name; +} + +function testCastingObjects3() returns error? { + var e = trap f7(); + return e is error ? e : (); +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_bir_test.bal b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_bir_test.bal index 7c9f597e4e39..28bd92404743 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_bir_test.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_bir_test.bal @@ -16,18 +16,17 @@ import testorg/foo.dependently_typed as rt; -type Person record { +type PersonDTBT record { readonly string name; int age; }; -type Employee record { - *Person; +type EmployeeDTBT record { + *PersonDTBT; string designation; }; -Person expPerson = {name: "John Doe", age: 20}; - +PersonDTBT expPerson = {name: "John Doe", age: 20}; // Test functions @@ -49,11 +48,11 @@ function testSimpleTypes() { } function testRecordVarRef() { - Person p = rt:getRecord(); + PersonDTBT p = rt:getRecord(); assert(expPerson, p); - Employee e = rt:getRecord(td = Employee); - assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); + EmployeeDTBT e = rt:getRecord(td = EmployeeDTBT); + assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); } function testVarRefInMapConstraint() { @@ -69,12 +68,12 @@ function testRuntimeCastError() { } function testVarRefUseInMultiplePlaces() { - [int, Person, float] tup1 = rt:getTuple(int, Person); - assert(<[int, Person, float]>[150, expPerson, 12.34], tup1); + [int, PersonDTBT, float] tup1 = rt:getTuple(int, PersonDTBT); + assert(<[int, PersonDTBT, float]>[150, expPerson, 12.34], tup1); } function testUnionTypes() { - int|Person u = rt:getVariedUnion(1, int, Person); + int|PersonDTBT u = rt:getVariedUnion(1, int, PersonDTBT); assert(expPerson, u); } @@ -84,7 +83,7 @@ function testArrayTypes() { } function testCastingForInvalidValues() { - int _ = rt:getInvalidValue(int, Person); + int _ = rt:getInvalidValue(int, PersonDTBT); } type XmlElement xml:Element; @@ -101,19 +100,21 @@ function testStream() { stream newSt = rt:getStream(st, string); string s = ""; - newSt.forEach(function (string x) { s += x; }); + newSt.forEach(function(string x) { + s += x; + }); assert("helloworldfromballerina", s); } function testTable() { - table key(name) tab = table [ - { name: "Chiran", age: 33, designation: "SE" }, - { name: "Mohan", age: 37, designation: "SE" }, - { name: "Gima", age: 38, designation: "SE" }, - { name: "Granier", age: 34, designation: "SE" } + table key(name) tab = table [ + {name: "Chiran", age: 33, designation: "SE"}, + {name: "Mohan", age: 37, designation: "SE"}, + {name: "Gima", age: 38, designation: "SE"}, + {name: "Granier", age: 34, designation: "SE"} ]; - table newTab = rt:getTable(tab, Person); + table newTab = rt:getTable(tab, PersonDTBT); assert(tab, newTab); } @@ -125,12 +126,12 @@ function testFunctionPointers() { } function testTypedesc() { - typedesc tP = rt:getTypedesc(Person); - assert(Person.toString(), tP.toString()); + typedesc tP = rt:getTypedesc(PersonDTBT); + assert(PersonDTBT.toString(), tP.toString()); } function testFuture() { - var fn = function (string name) returns string => "Hello " + name; + var fn = function(string name) returns string => "Hello " + name; future f = start fn("Pubudu"); future fNew = rt:getFuture(f, string); string res = checkpanic wait fNew; @@ -150,9 +151,12 @@ class PersonObj { function name() returns string => self.fname + " " + self.lname; } -type PersonTable table; +type PersonTable table; + type IntStream stream; + type IntArray int[]; + type XmlType xml; function testComplexTypes() { @@ -165,7 +169,7 @@ function testComplexTypes() { int[] ar = rt:echo([20, 30, 40], IntArray); assert([20, 30, 40], ar); - PersonObj pObj = new("John", "Doe"); + PersonObj pObj = new ("John", "Doe"); PersonObj nObj = rt:echo(pObj, PersonObj); assertSame(pObj, nObj); @@ -174,17 +178,19 @@ function testComplexTypes() { stream newSt = rt:echo(st, IntStream); int tot = 0; - newSt.forEach(function (int x1) { tot+= x1; }); + newSt.forEach(function(int x1) { + tot += x1; + }); assert(150, tot); - table key(name) tab = table [ - { name: "Chiran", age: 33}, - { name: "Mohan", age: 37}, - { name: "Gima", age: 38}, - { name: "Granier", age: 34} + table key(name) tab = table [ + {name: "Chiran", age: 33}, + {name: "Mohan", age: 37}, + {name: "Gima", age: 38}, + {name: "Granier", age: 34} ]; - table newTab = rt:echo(tab, PersonTable); + table newTab = rt:echo(tab, PersonTable); assert(tab, newTab); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_test.bal b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_test.bal index 49817c076771..bb681bdbc8c4 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_test.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/dependently_typed_functions_test.bal @@ -17,23 +17,24 @@ import ballerina/jballerina.java; type ItemType xml:Element|xml:Comment|xml:ProcessingInstruction|xml:Text; + type XmlElement xml:Element; -type Person record { +type PersonDTFT record { readonly string name; int age; }; -type Employee record { - *Person; +type EmployeeDTFT record { + *PersonDTFT; string designation; }; -Person expPerson = {name: "John Doe", age: 20}; +PersonDTFT expPerson = {name: "John Doe", age: 20}; -type Foo int|float; +type FooDTFT int|float; -type Foo2 Foo; +type Foo2DTFT FooDTFT; type AnnotationRecord record {| int minValue; @@ -52,7 +53,7 @@ type Foo3 int|float; minValue: 12, maxValue: 24 } -type Foo4 Foo; +type Foo4 FooDTFT; // Test functions @@ -74,31 +75,31 @@ function testSimpleTypes() { } function testReferredTypes() { - map annotationMapValue = getAnnotationValue(Foo); - AnnotationRecord? 'annotation = annotationMapValue["BarAnnotation"]; + map annotationMapValue = getAnnotationValue(FooDTFT); + AnnotationRecord? 'annotation = annotationMapValue["BarAnnotation"]; assert('annotation, ()); - map annotationMapValue2 = getAnnotationValue(Foo2); - AnnotationRecord? annotation2 = annotationMapValue2["BarAnnotation"]; + map annotationMapValue2 = getAnnotationValue(Foo2DTFT); + AnnotationRecord? annotation2 = annotationMapValue2["BarAnnotation"]; assert(annotation2, ()); map annotationMapValue3 = getAnnotationValue(Foo3); - AnnotationRecord annotation3 = annotationMapValue3["BarAnnotation"]; + AnnotationRecord annotation3 = annotationMapValue3["BarAnnotation"]; assert(annotation3, {minValue: 18, maxValue: 36}); assertTrue(getAnnotationValue2(1, Foo3, "BarAnnotation", 18, 36) is anydata); map annotationMapValue4 = getAnnotationValue(Foo4); - AnnotationRecord annotation4 = annotationMapValue4["BarAnnotation"]; - assert(annotation4, {minValue: 12, maxValue: 24}); + AnnotationRecord annotation4 = annotationMapValue4["BarAnnotation"]; + assert(annotation4, {minValue: 12, maxValue: 24}); assertTrue(getAnnotationValue2(1, Foo4, "BarAnnotation", 12, 24) is anydata); } function testRecordVarRef() { - Person p = getRecord(); + PersonDTFT p = getRecord(); assert(expPerson, p); - Employee e = getRecord(td = Employee); - assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); + EmployeeDTFT e = getRecord(td = EmployeeDTFT); + assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); } function testVarRefInMapConstraint() { @@ -114,12 +115,12 @@ function testRuntimeCastError() { } function testVarRefUseInMultiplePlaces() { - [int, Person, float] tup1 = getTuple(int, Person); - assert(<[int, Person, float]>[150, expPerson, 12.34], tup1); + [int, PersonDTFT, float] tup1 = getTuple(int, PersonDTFT); + assert(<[int, PersonDTFT, float]>[150, expPerson, 12.34], tup1); } function testUnionTypes() { - int|Person u = getVariedUnion(1, int, Person); + int|PersonDTFT u = getVariedUnion(1, int, PersonDTFT); assert(expPerson, u); } @@ -129,7 +130,7 @@ function testArrayTypes() { } function testCastingForInvalidValues() { - int x = getInvalidValue(int, Person); + int x = getInvalidValue(int, PersonDTFT); } function testXML() { @@ -144,19 +145,21 @@ function testStream() { stream newSt = getStream(st, string); string s = ""; - error? err = newSt.forEach(function (string x) { s += x; }); + error? err = newSt.forEach(function(string x) { + s += x; + }); assert("helloworldfromballerina", s); } function testTable() { - table key(name) tab = table [ - { name: "Chiran", age: 33, designation: "SE" }, - { name: "Mohan", age: 37, designation: "SE" }, - { name: "Gima", age: 38, designation: "SE" }, - { name: "Granier", age: 34, designation: "SE" } + table key(name) tab = table [ + {name: "Chiran", age: 33, designation: "SE"}, + {name: "Mohan", age: 37, designation: "SE"}, + {name: "Gima", age: 38, designation: "SE"}, + {name: "Granier", age: 34, designation: "SE"} ]; - table newTab = getTable(tab, Person); + table newTab = getTable(tab, PersonDTFT); assert(tab, newTab); } @@ -168,12 +171,12 @@ function testFunctionPointers() { } function testTypedesc() { - typedesc tP = getTypedesc(Person); - assert(Person.toString(), tP.toString()); + typedesc tP = getTypedesc(PersonDTFT); + assert(PersonDTFT.toString(), tP.toString()); } function testFuture() { - var fn = function (string name) returns string => "Hello " + name; + var fn = function(string name) returns string => "Hello " + name; future f = start fn("Pubudu"); future fNew = getFuture(f, string); string res = checkpanic wait fNew; @@ -211,7 +214,7 @@ class PersonObj { type IntStream stream; -type PersonTable table; +type PersonTable table; type IntArray int[]; @@ -227,7 +230,7 @@ function testComplexTypes() { int[] ar = echo([20, 30, 40], IntArray); assert([20, 30, 40], ar); - PersonObj pObj = new("John", "Doe"); + PersonObj pObj = new ("John", "Doe"); PersonObj nObj = echo(pObj, PersonObj); assertSame(pObj, nObj); @@ -236,22 +239,24 @@ function testComplexTypes() { stream newSt = echo(st, IntStream); int tot = 0; - error? err = newSt.forEach(function (int x1) { tot+= x1; }); + error? err = newSt.forEach(function(int x1) { + tot += x1; + }); assert(150, tot); - table key(name) tab = table [ - { name: "Chiran", age: 33}, - { name: "Mohan", age: 37}, - { name: "Gima", age: 38}, - { name: "Granier", age: 34} + table key(name) tab = table [ + {name: "Chiran", age: 33}, + {name: "Mohan", age: 37}, + {name: "Gima", age: 38}, + {name: "Granier", age: 34} ]; - table newTab = echo(tab, PersonTable); + table newTab = echo(tab, PersonTable); assert(tab, newTab); } function testObjectExternFunctions() { - PersonObj pObj = new("John", "Doe"); + PersonObj pObj = new ("John", "Doe"); string s = pObj.getObjectValue(string); assert("John Doe", s); s = pObj.getObjectValueWithTypeDescParam(string); @@ -275,7 +280,6 @@ function testFunctionAssignment() { x = fn(string); } - // Interop functions function getValue(typedesc td) returns td = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", @@ -283,20 +287,20 @@ function getValue(typedesc td) returns td = @j paramTypes: ["io.ballerina.runtime.api.values.BTypedesc"] } external; -function getAnnotationValue(typedesc y) returns map = +function getAnnotationValue(typedesc y) returns map = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getAnnotationValue" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getAnnotationValue" +} external; function getAnnotationValue2(anydata value, typedesc td = <>, string annotationName = "", - int min = 0, int max = 0) returns td|error = + int min = 0, int max = 0) returns td|error = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getAnnotationValue2" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getAnnotationValue2" +} external; -function getRecord(typedesc td = Person) returns td = @java:Method { +function getRecord(typedesc td = PersonDTFT) returns td = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", name: "getRecord", paramTypes: ["io.ballerina.runtime.api.values.BTypedesc"] @@ -314,7 +318,7 @@ function getTuple(typedesc td1, typedesc td2, typedesc td1, typedesc td2) returns (td1|td2) = @java:Method { +function getVariedUnion(int x, typedesc td1, typedesc td2) returns (td1|td2) = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", name: "getVariedUnion", paramTypes: ["long", "io.ballerina.runtime.api.values.BTypedesc", "io.ballerina.runtime.api.values.BTypedesc"] @@ -332,7 +336,7 @@ function getArrayInferred(typedesc td = <>) returns td[] = @java:Method paramTypes: ["io.ballerina.runtime.api.values.BTypedesc"] } external; -function getInvalidValue(typedesc td1, typedesc td2) returns td1 = @java:Method { +function getInvalidValue(typedesc td1, typedesc td2) returns td1 = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", name: "getInvalidValue", paramTypes: ["io.ballerina.runtime.api.values.BTypedesc", "io.ballerina.runtime.api.values.BTypedesc"] @@ -360,8 +364,11 @@ function getFunction(function (string|int) returns anydata fn, typedesc returns function (param) returns ret = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", name: "getFunction", - paramTypes: ["io.ballerina.runtime.api.values.BFunctionPointer", "io.ballerina.runtime.api.values.BTypedesc", - "io.ballerina.runtime.api.values.BTypedesc"] + paramTypes: [ + "io.ballerina.runtime.api.values.BFunctionPointer", + "io.ballerina.runtime.api.values.BTypedesc", + "io.ballerina.runtime.api.values.BTypedesc" + ] } external; function getTypedesc(typedesc td) returns typedesc = @java:Method { @@ -425,13 +432,13 @@ var outParameterObject = object OutParameter { function testDependentlyTypedMethodsWithObjectTypeInclusion() { OutParameterClass c1 = new; int|error v1 = c1.get(int); - assert(1234, checkpanic v1); + assert(1234, checkpanic v1); assertSame(c1.c, c1.get(float)); - assert("hello world", checkpanic c1.get(string)); + assert("hello world", checkpanic c1.get(string)); - assert(321, checkpanic outParameterObject.get(int)); + assert(321, checkpanic outParameterObject.get(int)); decimal|error v2 = outParameterObject.get(decimal); - assert(23.45d, checkpanic v2); + assert(23.45d, checkpanic v2); } public class Bar { @@ -509,49 +516,49 @@ public function testSubtypingWithDependentlyTypedMethods() { Baz baz = new; Qux qux = new; - assert(1, checkpanic bar.get(int)); - assert(2, checkpanic baz.get(int)); - assert(3, checkpanic qux.get(int)); + assert(1, checkpanic bar.get(int)); + assert(2, checkpanic baz.get(int)); + assert(3, checkpanic qux.get(int)); decimal|error v2 = bar.get(decimal); - assert(23.45d, checkpanic v2); + assert(23.45d, checkpanic v2); anydata|error v3 = baz.get(decimal); - assert(23.45d, checkpanic v3); + assert(23.45d, checkpanic v3); v2 = qux.get(decimal); - assert(23.45d, checkpanic v2); + assert(23.45d, checkpanic v2); Baz baz1 = bar; Bar bar1 = qux; - assert(1, checkpanic baz1.get(int)); - assert(3, checkpanic bar1.get(int)); + assert(1, checkpanic baz1.get(int)); + assert(3, checkpanic bar1.get(int)); - assert(true, bar is Baz); - assert(true, qux is Bar); - assert(true, bar is Qux); - assert(false, baz is Bar); - assert(false, new Quux() is Qux); - assert(false, qux is Quux); + assert(true, bar is Baz); + assert(true, qux is Bar); + assert(true, bar is Qux); + assert(false, baz is Bar); + assert(false, new Quux() is Qux); + assert(false, qux is Quux); Corge corge = new Grault(); - assert(200, checkpanic corge.get(int, string)); - assert("Hello World!", checkpanic corge.get(string, int)); + assert(200, checkpanic corge.get(int, string)); + assert("Hello World!", checkpanic corge.get(string, int)); Grault grault = new Corge(); - assert(100, checkpanic grault.get(int, string)); - assert("Hello World!", checkpanic grault.get(string, float)); + assert(100, checkpanic grault.get(int, string)); + assert("Hello World!", checkpanic grault.get(string, float)); - assert(true, new Corge() is Grault); - assert(true, new Grault() is Corge); - assert(false, new Corge() is Garply); - assert(false, new Garply() is Corge); - assert(false, new Grault() is Garply); - assert(false, new Garply() is Grault); + assert(true, new Corge() is Grault); + assert(true, new Grault() is Corge); + assert(true, new Corge() is Garply); + assert(true, new Garply() is Corge); + assert(true, new Grault() is Garply); + assert(true, new Garply() is Grault); } function getWithDefaultableParams(int|string x, int|string y = 1, typedesc z = int) returns z = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getWithDefaultableParams" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getWithDefaultableParams" +} external; function testDependentlyTypedFunctionWithDefaultableParams() { int a = getWithDefaultableParams(1); @@ -584,12 +591,12 @@ type IntOrString int|string; public function testStartActionWithDependentlyTypedFunctions() { Client cl = new; - var assert1 = function (future f) { + var assert1 = function(future f) { int|string|error r = wait f; assert(true, r is error); - error e = r; + error e = r; assert("Error!", e.message()); - assert("Union typedesc", checkpanic e.detail()["message"]); + assert("Union typedesc", checkpanic e.detail()["message"]); }; future a = start getWithUnion("", IntOrString); assert1(a); @@ -598,7 +605,7 @@ public function testStartActionWithDependentlyTypedFunctions() { future c = start cl->remoteGet("", IntOrString); assert1(c); - var assert2 = function (future f, int expected) { + var assert2 = function(future f, int expected) { int|error r = wait f; assert(true, r is int); assert(expected, checkpanic r); @@ -612,7 +619,7 @@ public function testStartActionWithDependentlyTypedFunctions() { future g = start cl->remoteGet("hi", int); assert2(g, 2); - var assert3 = function (future f, string expected) { + var assert3 = function(future f, string expected) { string|error r = wait f; assert(true, r is string); assert(expected, checkpanic r); @@ -627,9 +634,9 @@ public function testStartActionWithDependentlyTypedFunctions() { function getWithUnion(int|string x, typedesc y) returns y|error = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getWithUnion" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getWithUnion" +} external; client class Client { function get(int|string x, typedesc y = int) returns y|error = @java:Method { @@ -644,12 +651,12 @@ client class Client { } function getWithRestParam(int i, typedesc j, int... k) returns j|boolean = - @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" - } external; + @java:Method { + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" +} external; function getWithMultipleTypedescs(int i, typedesc j, typedesc k, typedesc... l) - returns j|k|boolean = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" } external; + returns j|k|boolean = @java:Method {'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType"} external; function testArgsForDependentlyTypedFunctionViaTupleRestArg() { [typedesc] a = [string]; @@ -728,7 +735,7 @@ type IJK record {| |}; function testArgsForDependentlyTypedFunctionViaRecordRestArg() { - record {| typedesc y; |} a = {y: string}; + record {|typedesc y;|} a = {y: string}; string|error b = getWithUnion(123, ...a); assert("123", checkpanic b); @@ -740,7 +747,7 @@ function testArgsForDependentlyTypedFunctionViaRecordRestArg() { string|boolean f = getWithRestParam(...e); assert(true, f); - record {| typedesc j = string; |} g = {}; + record {|typedesc j = string;|} g = {}; string|boolean h = getWithRestParam(1, ...g); assert(false, h); @@ -748,11 +755,11 @@ function testArgsForDependentlyTypedFunctionViaRecordRestArg() { int|string|boolean n = getWithMultipleTypedescs(...m); assert(true, n); - record {| typedesc j = string; typedesc k; |} o = {k: int}; + record {|typedesc j = string; typedesc k;|} o = {k: int}; int|string|boolean p = getWithMultipleTypedescs(1, ...o); assert(true, p); - record {| int i; typedesc j = byte; typedesc k; |} q = {i: 1, k: byte}; + record {|int i; typedesc j = byte; typedesc k;|} q = {i: 1, k: byte}; byte|boolean r = getWithMultipleTypedescs(...q); assert(true, r); } @@ -767,29 +774,29 @@ public type TargetType typedesc; public client class ClientWithMethodWithIncludedRecordParamAndDefaultableParams { remote function post(TargetType targetType = int, *ClientActionOptions options) returns @tainted targetType = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "clientPost" - } external; - + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "clientPost" + } external; + function calculate(int i, TargetType targetType = int, *ClientActionOptions options) returns @tainted targetType = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "calculate" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "calculate" + } external; } public client class ClientWithMethodWithIncludedRecordParamAndRequiredParams { remote function post(TargetType targetType, *ClientActionOptions options) returns @tainted targetType = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "clientPost" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "clientPost" + } external; function calculate(int i, TargetType targetType, *ClientActionOptions options) returns @tainted targetType|error = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "calculate" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "calculate" + } external; } function testDependentlyTypedFunctionWithIncludedRecordParam() { @@ -885,57 +892,58 @@ function testDependentlyTypedFunctionWithIncludedRecordParam() { client class ClientObjImpl { *ClientObject; - remote isolated function query(stream strm, typedesc rowType = <>) returns stream = + + remote isolated function query(stream strm, typedesc rowType = <>) returns stream = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getStreamOfRecords", - paramTypes: ["io.ballerina.runtime.api.values.BStream", "io.ballerina.runtime.api.values.BTypedesc"] - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getStreamOfRecords", + paramTypes: ["io.ballerina.runtime.api.values.BStream", "io.ballerina.runtime.api.values.BTypedesc"] + } external; } public type ClientObject client object { - remote isolated function query(stream strm, typedesc rowType = <>) returns stream ; + remote isolated function query(stream strm, typedesc rowType = <>) returns stream; }; function testDependentlyTypedMethodCallOnObjectType() { ClientObject cl = new ClientObjImpl(); - Person p1 = getRecord(); - Person p2 = getRecord(); - Person[] personList = [p1, p2]; - stream personStream = personList.toStream(); - stream y = cl->query(personStream, Person); + PersonDTFT p1 = getRecord(); + PersonDTFT p2 = getRecord(); + PersonDTFT[] personList = [p1, p2]; + stream personStream = personList.toStream(); + stream y = cl->query(personStream, PersonDTFT); var rec = y.next(); - if (rec is record {| Person value; |}) { - Person person = rec.value; + if (rec is record {|PersonDTFT value;|}) { + PersonDTFT person = rec.value; assert(20, person.age); assert("John Doe", person.name); } rec = y.next(); - assert(true, rec is record {| Person value; |}); + assert(true, rec is record {|PersonDTFT value;|}); } function testDependentlyTypedMethodCallOnObjectTypeWithInferredArgument() { ClientObject cl = new ClientObjImpl(); - Person p1 = getRecord(); - Person p2 = getRecord(); - Person[] personList = [p1, p2]; - stream personStream = personList.toStream(); - stream y = cl->query(personStream); + PersonDTFT p1 = getRecord(); + PersonDTFT p2 = getRecord(); + PersonDTFT[] personList = [p1, p2]; + stream personStream = personList.toStream(); + stream y = cl->query(personStream); var rec = y.next(); - if (rec is record {| Person value; |}) { - Person person = rec.value; + if (rec is record {|PersonDTFT value;|}) { + PersonDTFT person = rec.value; assert(20, person.age); assert("John Doe", person.name); } rec = y.next(); - assert(true, rec is record {| Person value; |}); + assert(true, rec is record {|PersonDTFT value;|}); } function functionWithInferredArgForParamOfTypeReferenceType(TargetType t = <>) returns t = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "functionWithInferredArgForParamOfTypeReferenceType" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "functionWithInferredArgForParamOfTypeReferenceType" +} external; function testDependentlyTypedFunctionWithInferredArgForParamOfTypeReferenceType() { int a = functionWithInferredArgForParamOfTypeReferenceType(); @@ -963,18 +971,18 @@ function testDependentlyTypedResourceMethods() { ClientWithExternalResourceBody cl = new ClientWithExternalResourceBody(); string|error a = cl->/games/carrom(targetType = string); assert("[\"games\",\"carrom\"]", checkpanic a); - + var cl2 = client object { resource function get [string... path](TargetType targetType = <>) returns targetType|error = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", - name: "getResource" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", + name: "getResource" + } external; }; string|error b = cl2->/games/football(targetType = string); assert("[\"games\",\"football\"]", checkpanic b); - + int|error c = cl2->/games/football(); assert(0, checkpanic c); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/inferred_dependently_typed_func_signature.bal b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/inferred_dependently_typed_func_signature.bal index d1e9afa45b5e..06beac3920e1 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/inferred_dependently_typed_func_signature.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/javainterop/inferred_dependently_typed_func_signature.bal @@ -16,18 +16,17 @@ import ballerina/jballerina.java; -type Person record { +type PersonIDTF record { readonly string name; int age; }; -type Employee record { - *Person; +type EmployeeIDTF record { + *PersonIDTF; string designation; }; -Person expPerson = {name: "John Doe", age: 20}; - +PersonIDTF expPerson = {name: "John Doe", age: 20}; // Test functions @@ -49,11 +48,11 @@ function testSimpleTypes() { } function testRecordVarRef() { - Person p = getRecord(); + PersonIDTF p = getRecord(); assert(expPerson, p); - Employee e = getRecord(td = Employee); - assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); + EmployeeIDTF e = getRecord(td = EmployeeIDTF); + assert({name: "Jane Doe", age: 25, designation: "Software Engineer"}, e); } function testVarRefInMapConstraint() { @@ -69,15 +68,15 @@ function testRuntimeCastError() { error err = m1; assert("{ballerina}TypeCastError", err.message()); - assert("incompatible types: 'map' cannot be cast to 'map'", checkpanic err.detail()["message"]); + assert("incompatible types: 'map' cannot be cast to 'map'", checkpanic err.detail()["message"]); } function testTupleTypes() { - [int, Person, float] tup1 = getTuple(int, Person); - assert(<[int, Person, float]>[150, expPerson, 12.34], tup1); + [int, PersonIDTF, float] tup1 = getTuple(int, PersonIDTF); + assert(<[int, PersonIDTF, float]>[150, expPerson, 12.34], tup1); - [int, Person, boolean...] tup2 = getTupleWithRestDesc(int, Person); - assert(<[int, Person, boolean...]>[150, expPerson, true, true], tup2); + [int, PersonIDTF, boolean...] tup2 = getTupleWithRestDesc(int, PersonIDTF); + assert(<[int, PersonIDTF, boolean...]>[150, expPerson, true, true], tup2); tup2[4] = false; assert(5, tup2.length()); } @@ -88,11 +87,12 @@ function testArrayTypes() { } type XmlComment xml:Comment; + type XmlElement xml:Element; function testXml() { xml:Comment x1 = xml ``; - xml x2 = > x1.concat(xml ``); + xml x2 = >x1.concat(xml ``); xml a = getXml(val = x2); assert(x2, a); assert(2, a.length()); @@ -128,14 +128,14 @@ function testXml() { function testCastingForInvalidValues() { var fn = function() { - int x = getInvalidValue(td2 = Person); + int x = getInvalidValue(td2 = PersonIDTF); }; error? y = trap fn(); assert(true, y is error); - error err = y; + error err = y; assert("{ballerina}TypeCastError", err.message()); - assert("incompatible types: 'Person' cannot be cast to 'int'", checkpanic err.detail()["message"]); + assert("incompatible types: 'PersonIDTF' cannot be cast to 'int'", checkpanic err.detail()["message"]); } function testStream() { @@ -144,19 +144,21 @@ function testStream() { stream newSt = getStream(st); string s = ""; - error? err = newSt.forEach(function (string x) { s += x; }); + error? err = newSt.forEach(function(string x) { + s += x; + }); assert("helloworldfromballerina", s); } function testTable() { - table key(name) tab = table [ - { name: "Chiran", age: 33, designation: "SE" }, - { name: "Mohan", age: 37, designation: "SE" }, - { name: "Gima", age: 38, designation: "SE" }, - { name: "Granier", age: 34, designation: "SE" } + table key(name) tab = table [ + {name: "Chiran", age: 33, designation: "SE"}, + {name: "Mohan", age: 37, designation: "SE"}, + {name: "Gima", age: 38, designation: "SE"}, + {name: "Granier", age: 34, designation: "SE"} ]; - table newTab = getTable(tab); + table newTab = getTable(tab); assert(tab, newTab); } @@ -168,12 +170,12 @@ function testFunctionPointers() { } function testTypedesc() { - typedesc tP = getTypedesc(); - assert(Person.toString(), tP.toString()); + typedesc tP = getTypedesc(); + assert(PersonIDTF.toString(), tP.toString()); } function testFuture() { - var fn = function (string name) returns string => "Hello " + name; + var fn = function(string name) returns string => "Hello " + name; future f = start fn("Pubudu"); future fNew = getFuture(f, string); string res = checkpanic wait fNew; @@ -195,7 +197,7 @@ class PersonObj { type IntStream stream; -type PersonTable table; +type PersonTable table; type IntArray int[]; @@ -209,7 +211,7 @@ function testComplexTypes() { int[] ar = echo([20, 30, 40]); assert([20, 30, 40], ar); - PersonObj pObj = new("John", "Doe"); + PersonObj pObj = new ("John", "Doe"); PersonObj nObj = echo(pObj); assertSame(pObj, nObj); @@ -218,17 +220,19 @@ function testComplexTypes() { stream newSt = echo(st); int tot = 0; - error? err = newSt.forEach(function (int x1) { tot+= x1; }); + error? err = newSt.forEach(function(int x1) { + tot += x1; + }); assert(150, tot); - table key(name) tab = table [ - { name: "Chiran", age: 33}, - { name: "Mohan", age: 37}, - { name: "Gima", age: 38}, - { name: "Granier", age: 34} + table key(name) tab = table [ + {name: "Chiran", age: 33}, + {name: "Mohan", age: 37}, + {name: "Gima", age: 38}, + {name: "Granier", age: 34} ]; - table newTab = echo(tab); + table newTab = echo(tab); assert(tab, newTab); } @@ -244,7 +248,7 @@ function testFunctionAssignment() { error err = v; assert("{ballerina}TypeCastError", err.message()); - assert("incompatible types: 'string' cannot be cast to 'int'", checkpanic err.detail()["message"]); + assert("incompatible types: 'string' cannot be cast to 'int'", checkpanic err.detail()["message"]); } function testUnionTypes() { @@ -261,10 +265,10 @@ function testUnionTypes() { assert("hello", d); int[]|map? e = getComplexUnion(); - assert( [1, 2], e); + assert([1, 2], e); map<[int, string][]>|()|[int, string][] f = getComplexUnion(); - assert(> {entry: [[100, "Hello World"]]}, f); + assert(>{entry: [[100, "Hello World"]]}, f); int|boolean? g = getSimpleUnion(1, int); assert(1, g); @@ -279,47 +283,47 @@ function testUnionTypes() { assert("hello", j); int[]|map? k = getComplexUnion(int); - assert( [1, 2], k); + assert([1, 2], k); map<[int, string][]>|()|[int, string][] l = getComplexUnion(td = [int, string]); - assert(> {entry: [[100, "Hello World"]]}, l); + assert(>{entry: [[100, "Hello World"]]}, l); } function testArgCombinations() { int[] a = funcWithMultipleArgs(1, int, ["hello", "world"]); - assert( [2, 1], a); + assert([2, 1], a); string[] b = funcWithMultipleArgs(td = string, arr = ["hello", "world"], i = 3); - assert( ["hello", "world", "3"], b); + assert(["hello", "world", "3"], b); - record {| string[] arr = ["hello", "world", "Ballerina"]; int i = 123; typedesc td; |} rec1 = {td: int}; + record {|string[] arr = ["hello", "world", "Ballerina"]; int i = 123; typedesc td;|} rec1 = {td: int}; int[] c = funcWithMultipleArgs(...rec1); - assert( [3, 123], c); + assert([3, 123], c); - record {| string[] arr = ["hello", "world"]; |} rec2 = {}; + record {|string[] arr = ["hello", "world"];|} rec2 = {}; int[] d = funcWithMultipleArgs(1234, int, ...rec2); - assert( [2, 1234], d); + assert([2, 1234], d); [int, typedesc, string[]] tup1 = [21, string, ["hello"]]; string[] e = funcWithMultipleArgs(...tup1); - assert( ["hello", "21"], e); + assert(["hello", "21"], e); [string[]] tup2 = [["hello"]]; string[] f = funcWithMultipleArgs(34, string, ...tup2); - assert( ["hello", "34"], f); + assert(["hello", "34"], f); int[] g = funcWithMultipleArgs(1); - assert( [0, 1], g); + assert([0, 1], g); string[] h = funcWithMultipleArgs(101, arr = ["hello", "world"]); - assert( ["hello", "world", "101"], h); + assert(["hello", "world", "101"], h); int[] i = funcWithMultipleArgs(arr = ["hello", "world"], i = 202); - assert( [2, 202], i); + assert([2, 202], i); } function testBuiltInRefType() { - stream strm = ( [1, 2, 3]).toStream(); + stream strm = ([1, 2, 3]).toStream(); readonly|handle|stream a = funcReturningUnionWithBuiltInRefType(strm); assertSame(strm, a); @@ -331,65 +335,65 @@ function testBuiltInRefType() { assertSame(strm, d); stream|readonly e = funcReturningUnionWithBuiltInRefType(strm); assert(true, e is handle); - string? str = java:toString( checkpanic e); + string? str = java:toString(checkpanic e); assert("hello world", str); } function testParameterizedTypeInUnionWithNonParameterizedTypes() { - record {| stream x; |} rec = {x: ( [1, 2, 3]).toStream()}; - object {}|record {| stream x; |}|int|error a = getValueWithUnionReturnType(rec); - assert(101, checkpanic a); + record {|stream x;|} rec = {x: ([1, 2, 3]).toStream()}; + object {}|record {|stream x;|}|int|error a = getValueWithUnionReturnType(rec); + assert(101, checkpanic a); PersonObj pObj = new ("John", "Doe"); - object {}|record {| stream x; |}|string[]|error b = getValueWithUnionReturnType(pObj); + object {}|record {|stream x;|}|string[]|error b = getValueWithUnionReturnType(pObj); assertSame(pObj, b); - error|object {}|record {| stream x; |}|boolean c = getValueWithUnionReturnType(true, boolean); - assert(false, checkpanic c); + error|object {}|record {|stream x;|}|boolean c = getValueWithUnionReturnType(true, boolean); + assert(false, checkpanic c); - error|object {}|record {| stream x; |}|boolean d = getValueWithUnionReturnType(td = boolean, val = false); - assert(true, checkpanic d); + error|object {}|record {|stream x;|}|boolean d = getValueWithUnionReturnType(td = boolean, val = false); + assert(true, checkpanic d); } function testUsageWithVarWithUserSpecifiedArg() { - stream strm = ( [1, 2, 3]).toStream(); + stream strm = ([1, 2, 3]).toStream(); var x = funcReturningUnionWithBuiltInRefType(strm, IntStream); assertSame(strm, x); } - function testFunctionWithAnyFunctionParamType() { - var fn = function (function x, int y) { +function testFunctionWithAnyFunctionParamType() { + var fn = function(function x, int y) { }; function (function, int) z = getFunctionWithAnyFunctionParamType(fn); assertSame(fn, z); - } +} function testUsageWithCasts() { - int a = getValue(); + int a = getValue(); assert(150, a); - var b = getValue(); + var b = getValue(); assert(12.34, b); - any c = getValue(); - assert(23.45d, c); + any c = getValue(); + assert(23.45d, c); - string|xml|float d = getValue(); + string|xml|float d = getValue(); assert("Hello World!", d); - anydata e = getValue(); + anydata e = getValue(); assert(true, e); - anydata f = <[int, Person, boolean...]> getTupleWithRestDesc(int, Person); - assert(<[int, Person, boolean...]>[150, expPerson, true, true], f); + anydata f = <[int, PersonIDTF, boolean...]>getTupleWithRestDesc(int, PersonIDTF); + assert(<[int, PersonIDTF, boolean...]>[150, expPerson, true, true], f); - record {| stream x; |} g = {x: ( [1, 2, 3]).toStream()}; - var h = x; |}|int> checkpanic getValueWithUnionReturnType(g); - assert(101, h); + record {|stream x;|} g = {x: ([1, 2, 3]).toStream()}; + var h = x;|}|int>checkpanic getValueWithUnionReturnType(g); + assert(101, h); PersonObj i = new ("John", "Doe"); - any|error j = x; |}|string[]|error> getValueWithUnionReturnType(i); + any|error j = x;|}|string[]|error>getValueWithUnionReturnType(i); assertSame(i, j); } @@ -420,7 +424,7 @@ function getTuple(typedesc td1, typedesc td2, typedesc td1, typedesc td2, typedesc td3 = <>) returns [td1, td2, td3...] = - @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" } external; + @java:Method {'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType"} external; function getArray(typedesc td = <>) returns td[] = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", @@ -430,10 +434,10 @@ function getArray(typedesc td = <>) returns td[] = @java:Method { function getXml(typedesc td = <>, xml val = xml ``) returns xml = @java:Method { - 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" - } external; + 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType" +} external; -function getInvalidValue(typedesc td1 = <>, typedesc td2 = Person) returns td1 = +function getInvalidValue(typedesc td1 = <>, typedesc td2 = PersonIDTF) returns td1 = @java:Method { 'class: "org.ballerinalang.nativeimpl.jvm.tests.VariableReturnType", name: "getInvalidValue", diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/jvm/objects_subtyping.bal b/tests/jballerina-unit-test/src/test/resources/test-src/jvm/objects_subtyping.bal index b0aa1255ab05..7d9ea39b789a 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/jvm/objects_subtyping.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/jvm/objects_subtyping.bal @@ -161,9 +161,7 @@ public function testObjectAssignabilityBetweenNonClientAndClientObject() { subtyping:ClientObjectWithoutRemoteMethod o2 = new subtyping:ClientObjectWithoutRemoteMethod("ClientObjectWithoutRemoteMethod"); subtyping:NonClientObject obj3 = o2; - subtyping:ClientObjectWithoutRemoteMethod obj4 = obj1; - assertEquality("NonClientObject", obj4.name); assertEquality("ClientObjectWithoutRemoteMethod", obj3.name); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/object/ObjectEquivalencyProject/object-equivalency.bal b/tests/jballerina-unit-test/src/test/resources/test-src/object/ObjectEquivalencyProject/object-equivalency.bal index 0a9988ceb3b9..db8c5066fc87 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/object/ObjectEquivalencyProject/object-equivalency.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/object/ObjectEquivalencyProject/object-equivalency.bal @@ -654,13 +654,9 @@ client class ClientObjectWithoutRemoteMethod { } function testObjectAssignabilityBetweenNonClientAndClientObject() { - NonClientObject obj1 = new("NonClientObject"); - ClientObjectWithoutRemoteMethod obj2 = new("ClientObjectWithoutRemoteMethod"); + ClientObjectWithoutRemoteMethod obj2 = new ("ClientObjectWithoutRemoteMethod"); NonClientObject obj3 = obj2; - ClientObjectWithoutRemoteMethod obj4 = obj1; - - assertEquality("NonClientObject", obj4.name); assertEquality("ClientObjectWithoutRemoteMethod", obj3.name); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/multiple-order-by-clauses.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/multiple-order-by-clauses.bal index 9fd7422c55fc..c6ae94051aa2 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/multiple-order-by-clauses.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/multiple-order-by-clauses.bal @@ -14,59 +14,59 @@ // specific language governing permissions and limitations // under the License. -type Student record {| - readonly int id; - string? fname; - float fee; - decimal impact; - boolean isUndergrad; +type StudentY record {| + readonly int id; + string? fname; + float fee; + decimal impact; + boolean isUndergrad; |}; -type Person record {| +type PersonY record {| string firstName; string lastName; int age; string address; |}; -type Customer record {| +type CustomerY record {| readonly int id; readonly string name; int noOfItems; |}; -type CustomerProfile record {| +type CustomerProfileY record {| string name; int age; int noOfItems; string address; |}; -type StudentTable table key(id); +type StudentTableY table key(id); function testQueryExprWithMultipleOrderByClauses() returns boolean { boolean testPassed = true; - Student s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student s3 = {id: 2, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; - Student s4 = {id: 2, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s5 = {id: 3, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s3 = {id: 2, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; + StudentY s4 = {id: 2, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s5 = {id: 3, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student[] studentList = [s1, s2, s3, s4, s5]; + StudentY[] studentList = [s1, s2, s3, s4, s5]; - Student[] opStudentList = + StudentY[] opStudentList = from var student in studentList - order by student.fname descending, student.impact - order by student.id descending - select student; + order by student.fname descending, student.impact + order by student.id descending + select student; testPassed = testPassed && opStudentList.length() == 5; - testPassed = testPassed && opStudentList[0] == studentList[4]; - testPassed = testPassed && opStudentList[1] == studentList[3]; - testPassed = testPassed && opStudentList[2] == studentList[1]; - testPassed = testPassed && opStudentList[3] == studentList[2]; - testPassed = testPassed && opStudentList[4] == studentList[0]; + testPassed = testPassed && opStudentList[0] == studentList[4]; + testPassed = testPassed && opStudentList[1] == studentList[3]; + testPassed = testPassed && opStudentList[2] == studentList[1]; + testPassed = testPassed && opStudentList[3] == studentList[2]; + testPassed = testPassed && opStudentList[4] == studentList[0]; return testPassed; } @@ -74,32 +74,32 @@ function testQueryExprWithMultipleOrderByClauses() returns boolean { function testQueryExprWithMultipleFromAndMultipleOrderByClauses() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 7, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerY c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerY c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerY c3 = {id: 7, name: "James", noOfItems: 25}; + CustomerY c4 = {id: 0, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23, address: "New York"}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30, address: "California"}; + PersonY p1 = {firstName: "Amy", lastName: "Melina", age: 23, address: "New York"}; + PersonY p2 = {firstName: "Frank", lastName: "James", age: 30, address: "California"}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerY[] customerList = [c1, c2, c3, c4]; + PersonY[] personList = [p1, p2]; - Customer[] opCustomerList = + CustomerY[] opCustomerList = from var customer in customerList - from var person in personList - let string customerName = "Johns" - where person.lastName == customer.name - order by customer.id descending - order by person.address - select { - id: customer.id, - name: customerName, - noOfItems: customer.noOfItems - }; + from var person in personList + let string customerName = "Johns" + where person.lastName == customer.name + order by customer.id descending + order by person.address + select { + id: customer.id, + name: customerName, + noOfItems: customer.noOfItems + }; testPassed = testPassed && opCustomerList.length() == 4; - Customer cus; + CustomerY cus; cus = opCustomerList[0]; testPassed = testPassed && cus.id == 7 && cus.noOfItems == 25; cus = opCustomerList[1]; @@ -114,32 +114,32 @@ function testQueryExprWithMultipleFromAndMultipleOrderByClauses() returns boolea function testQueryExprWithJoinAndMultipleOrderByClauses() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "James", noOfItems: 25}; - Customer c4 = {id: 4, name: "James", noOfItems: 25}; + CustomerY c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerY c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerY c3 = {id: 3, name: "James", noOfItems: 25}; + CustomerY c4 = {id: 4, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23, address: "New York"}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30, address: "California"}; + PersonY p1 = {firstName: "Amy", lastName: "Melina", age: 23, address: "New York"}; + PersonY p2 = {firstName: "Frank", lastName: "James", age: 30, address: "California"}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerY[] customerList = [c1, c2, c3, c4]; + PersonY[] personList = [p1, p2]; - CustomerProfile[] customerProfileList = + CustomerProfileY[] customerProfileList = from var customer in customerList - join var person in personList + join var person in personList on customer.name equals person.lastName - order by customer.noOfItems descending - order by person.address - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems, - address: person.address - }; + order by customer.noOfItems descending + order by person.address + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems, + address: person.address + }; testPassed = testPassed && customerProfileList.length() == 4; - CustomerProfile cp; + CustomerProfileY cp; cp = customerProfileList[0]; testPassed = testPassed && cp.name == "Frank" && cp.age == 30 && cp.noOfItems == 25 && cp.address == "California"; cp = customerProfileList[1]; @@ -154,36 +154,40 @@ function testQueryExprWithJoinAndMultipleOrderByClauses() returns boolean { function testQueryExprWithInnerQueriesAndMultipleOrderByClauses() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; + CustomerY c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerY c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerY c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerY c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerY c5 = {id: 2, name: "James", noOfItems: 30}; - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; + PersonY p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; + PersonY p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; + PersonY p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; - Customer[] customerList = [c1, c2, c3, c4, c5]; - Person[] personList = [p1, p2, p3]; + CustomerY[] customerList = [c1, c2, c3, c4, c5]; + PersonY[] personList = [p1, p2, p3]; - CustomerProfile[] customerProfileList = + CustomerProfileY[] customerProfileList = from var customer in (stream from var c in customerList - order by c.id descending limit 4 select c) - join var person in (from var p in personList order by p.firstName descending select p) - on customer.name equals person.lastName - order by customer.noOfItems descending - order by person.address + order by c.id descending limit 4 - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems, - address: person.address - }; + select c) + join var person in (from var p in personList + order by p.firstName descending + select p) + on customer.name equals person.lastName + order by customer.noOfItems descending + order by person.address + limit 4 + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems, + address: person.address + }; testPassed = testPassed && customerProfileList.length() == 4; - CustomerProfile cp; + CustomerProfileY cp; cp = customerProfileList[0]; testPassed = testPassed && cp.name == "Jennifer" && cp.age == 23 && cp.noOfItems == 62 && cp.address == "California"; @@ -199,37 +203,40 @@ function testQueryExprWithInnerQueriesAndMultipleOrderByClauses() returns boolea function testQueryExprWithMultipleOrderByAndMultipleLimitClauses() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; + CustomerY c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerY c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerY c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerY c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerY c5 = {id: 2, name: "James", noOfItems: 30}; - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; + PersonY p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; + PersonY p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; + PersonY p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; - Customer[] customerList = [c1, c2, c3, c4, c5]; - Person[] personList = [p1, p2, p3]; + CustomerY[] customerList = [c1, c2, c3, c4, c5]; + PersonY[] personList = [p1, p2, p3]; - CustomerProfile[] customerProfileList = + CustomerProfileY[] customerProfileList = from var customer in (stream from var c in customerList - order by c.id descending select c) - join var person in (from var p in personList order by p.firstName descending select p) + order by c.id descending + select c) + join var person in (from var p in personList + order by p.firstName descending + select p) on customer.name equals person.lastName - order by customer.noOfItems descending - limit 5 - order by person.address - limit 2 - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems, - address: person.address - }; + order by customer.noOfItems descending + limit 5 + order by person.address + limit 2 + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems, + address: person.address + }; testPassed = testPassed && customerProfileList.length() == 2; - CustomerProfile cp; + CustomerProfileY cp; cp = customerProfileList[0]; testPassed = testPassed && cp.name == "Jennifer" && cp.age == 23 && cp.noOfItems == 62 && cp.address == "California"; @@ -240,37 +247,41 @@ function testQueryExprWithMultipleOrderByAndMultipleLimitClauses() returns boole function testQueryActionWithMultipleOrderByClauses() returns boolean { boolean testPassed = true; - CustomerProfile[] customerProfileList = []; + CustomerProfileY[] customerProfileList = []; - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; + CustomerY c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerY c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerY c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerY c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerY c5 = {id: 2, name: "James", noOfItems: 30}; - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; + PersonY p1 = {firstName: "Jennifer", lastName: "Melina", age: 23, address: "California"}; + PersonY p2 = {firstName: "Frank", lastName: "James", age: 30, address: "New York"}; + PersonY p3 = {firstName: "Zeth", lastName: "James", age: 50, address: "Texas"}; - Customer[] customerList = [c1, c2, c3, c4, c5]; - Person[] personList = [p1, p2, p3]; + CustomerY[] customerList = [c1, c2, c3, c4, c5]; + PersonY[] personList = [p1, p2, p3]; error? op = from var customer in customerList - join var person in personList + join var person in personList on customer.name equals person.lastName - order by customer.noOfItems descending - limit 5 - order by person.address - limit 2 - do { - CustomerProfile cp = {name: person.firstName, age : person.age, noOfItems: customer.noOfItems, - address: person.address}; - customerProfileList[customerProfileList.length()] = cp; + order by customer.noOfItems descending + limit 5 + order by person.address + limit 2 + do { + CustomerProfileY cp = { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems, + address: person.address }; + customerProfileList[customerProfileList.length()] = cp; + }; testPassed = testPassed && customerProfileList.length() == 2; - CustomerProfile cp; + CustomerProfileY cp; cp = customerProfileList[0]; testPassed = testPassed && cp.name == "Jennifer" && cp.age == 23 && cp.noOfItems == 62 && cp.address == "California"; @@ -282,27 +293,27 @@ function testQueryActionWithMultipleOrderByClauses() returns boolean { function testQueryExprWithMultipleOrderByClausesReturnTable() returns boolean { boolean testPassed = true; - Student s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student s3 = {id: 9, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; - Student s4 = {id: 4, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s5 = {id: 10, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s3 = {id: 9, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; + StudentY s4 = {id: 4, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s5 = {id: 10, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student[] studentList = [s1, s2, s3, s4, s5]; + StudentY[] studentList = [s1, s2, s3, s4, s5]; - StudentTable|error opStudentTable = + StudentTableY|error opStudentTable = table key(id) from var student in studentList - order by student.fname descending, student.impact - order by student.id descending - select student; - - if (opStudentTable is StudentTable) { - Student[] opStudentList = opStudentTable.toArray(); - testPassed = testPassed && opStudentList[0] == studentList[4]; - testPassed = testPassed && opStudentList[1] == studentList[2]; - testPassed = testPassed && opStudentList[2] == studentList[3]; - testPassed = testPassed && opStudentList[3] == studentList[1]; - testPassed = testPassed && opStudentList[4] == studentList[0]; + order by student.fname descending, student.impact + order by student.id descending + select student; + + if (opStudentTable is StudentTableY) { + StudentY[] opStudentList = opStudentTable.toArray(); + testPassed = testPassed && opStudentList[0] == studentList[4]; + testPassed = testPassed && opStudentList[1] == studentList[2]; + testPassed = testPassed && opStudentList[2] == studentList[3]; + testPassed = testPassed && opStudentList[3] == studentList[1]; + testPassed = testPassed && opStudentList[4] == studentList[0]; } return testPassed; } @@ -310,23 +321,23 @@ function testQueryExprWithMultipleOrderByClausesReturnTable() returns boolean { function testQueryExprWithMultipleOrderByClausesReturnStream() returns boolean { boolean testPassed = true; - Student s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student s3 = {id: 9, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; - Student s4 = {id: 4, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; - Student s5 = {id: 10, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s1 = {id: 1, fname: "John", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s2 = {id: 2, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; + StudentY s3 = {id: 9, fname: (), fee: 4000.56, impact: 0.4, isUndergrad: true}; + StudentY s4 = {id: 4, fname: "Kate", fee: 2000.56, impact: 0.4, isUndergrad: true}; + StudentY s5 = {id: 10, fname: "John", fee: 2000.56, impact: 0.45, isUndergrad: true}; - Student[] studentList = [s1, s2, s3, s4, s5]; + StudentY[] studentList = [s1, s2, s3, s4, s5]; - stream opStudentStream = + stream opStudentStream = stream from var student in studentList - order by student.fname descending, student.impact - order by student.id descending - select student; + order by student.fname descending, student.impact + order by student.id descending + select student; - Student[] opStudentList = []; - record {| Student value; |}|error? v = opStudentStream.next(); - while (v is record {| Student value; |}) { + StudentY[] opStudentList = []; + record {|StudentY value;|}|error? v = opStudentStream.next(); + while (v is record {|StudentY value;|}) { opStudentList.push(v.value); v = opStudentStream.next(); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal index f1edb6added6..8bda7094c615 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/order-by-clause.bal @@ -22,13 +22,13 @@ type Student record {| boolean isUndergrad; |}; -type Person record {| +type PersonPos record {| string firstName; string lastName; int age; |}; -type Customer record {| +type CustomerPos record {| readonly int id; readonly string name; int noOfItems; @@ -57,25 +57,25 @@ type PaymentInfo record {| string modeOfPayment; |}; -type CustomerTable table key(id, name); +type CustomerTable table key(id, name); type CustomerValue record {| - Customer value; + CustomerPos value; |}; type PersonValue record {| - Person value; + PersonPos value; |}; -function getCustomer(record {| Customer value; |}? returnedVal) returns Customer? { +function getCustomer(record {|CustomerPos value;|}? returnedVal) returns CustomerPos? { if (returnedVal is CustomerValue) { - return returnedVal.value; + return returnedVal.value; } else { - return (); + return (); } } -function getPersonValue((record {| Person value; |}|error?)|(record {| Person value; |}?) returnedVal) +function getPersonValue((record {|PersonPos value;|}|error?)|(record {|PersonPos value;|}?) returnedVal) returns PersonValue? { var result = returnedVal; if (result is PersonValue) { @@ -96,13 +96,13 @@ function testQueryExprWithOrderByClause() returns boolean { Student[] studentList = [s1, s2, s3, s4]; Student[] opStudentList = from var student in studentList - order by student.fname descending, student.impact - select student; + order by student.fname descending, student.impact + select student; - testPassed = testPassed && opStudentList[0] == studentList[3]; - testPassed = testPassed && opStudentList[1] == studentList[0]; - testPassed = testPassed && opStudentList[2] == studentList[1]; - testPassed = testPassed && opStudentList[3] == studentList[2]; + testPassed = testPassed && opStudentList[0] == studentList[3]; + testPassed = testPassed && opStudentList[1] == studentList[0]; + testPassed = testPassed && opStudentList[2] == studentList[1]; + testPassed = testPassed && opStudentList[3] == studentList[2]; return testPassed; } @@ -118,71 +118,71 @@ function testQueryExprWithOrderByClause2() returns boolean { Student[] studentList = [s1, s2, s3, s4]; Student[] opStudentList = from var student in studentList - order by student.isUndergrad ascending, student.fee - select student; + order by student.isUndergrad ascending, student.fee + select student; - testPassed = testPassed && opStudentList[0] == studentList[1]; - testPassed = testPassed && opStudentList[1] == studentList[2]; - testPassed = testPassed && opStudentList[2] == studentList[0]; - testPassed = testPassed && opStudentList[3] == studentList[3]; + testPassed = testPassed && opStudentList[0] == studentList[1]; + testPassed = testPassed && opStudentList[1] == studentList[2]; + testPassed = testPassed && opStudentList[2] == studentList[0]; + testPassed = testPassed && opStudentList[3] == studentList[3]; return testPassed; } -function testQueryExprWithOrderByClause3() returns Customer[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 7, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; - - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; - - Customer[] opCustomerList = from var customer in customerList - from var person in personList - let string customerName = "Johns" - where person.lastName == "James" - order by customer.id descending - select { - id: customer.id, - name: customerName, - noOfItems: customer.noOfItems - }; - - return opCustomerList; +function testQueryExprWithOrderByClause3() returns CustomerPos[] { + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerPos c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 7, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 0, name: "James", noOfItems: 25}; + + PersonPos p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; + + CustomerPos[] customerList = [c1, c2, c3, c4]; + PersonPos[] personList = [p1, p2]; + + CustomerPos[] opCustomerList = from var customer in customerList + from var person in personList + let string customerName = "Johns" + where person.lastName == "James" + order by customer.id descending + select { + id: customer.id, + name: customerName, + noOfItems: customer.noOfItems + }; + + return opCustomerList; } function testQueryExprWithOrderByClauseReturnTable() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "James", noOfItems: 25}; - Customer c4 = {id: 4, name: "James", noOfItems: 25}; + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerPos c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 3, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 4, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerPos[] customerList = [c1, c2, c3, c4]; + PersonPos[] personList = [p1, p2]; CustomerTable|error customerTable = table key(id, name) from var customer in customerList - from var person in personList - where person.firstName == "Frank" - order by customer.noOfItems descending, customer.id - limit 3 - select { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - }; + from var person in personList + where person.firstName == "Frank" + order by customer.noOfItems descending, customer.id + limit 3 + select { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + }; if (customerTable is CustomerTable) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerPos? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[2]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[3]; @@ -198,19 +198,19 @@ function testQueryExprWithOrderByClauseReturnTable() returns boolean { function testQueryExprWithOrderByClauseReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p2 = {firstName: "John", lastName: "David", age: 33}; - Person p3 = {firstName: "John", lastName: "Fonseka", age: 28}; - Person p4 = {firstName: "John", lastName: "Fonseka", age: 30}; - Person p5 = {firstName: "John", lastName: "Fonseka", age: 20}; + PersonPos p1 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonPos p2 = {firstName: "John", lastName: "David", age: 33}; + PersonPos p3 = {firstName: "John", lastName: "Fonseka", age: 28}; + PersonPos p4 = {firstName: "John", lastName: "Fonseka", age: 30}; + PersonPos p5 = {firstName: "John", lastName: "Fonseka", age: 20}; - Customer c1 = {id: 1, name: "John", noOfItems: 25}; - Customer c2 = {id: 2, name: "Frank", noOfItems: 20}; + CustomerPos c1 = {id: 1, name: "John", noOfItems: 25}; + CustomerPos c2 = {id: 2, name: "Frank", noOfItems: 20}; - Person[] personList = [p1, p2, p3, p4, p5]; - Customer[] customerList = [c1, c2]; + PersonPos[] personList = [p1, p2, p3, p4, p5]; + CustomerPos[] customerList = [c1, c2]; - stream outputPersonStream = stream from var person in personList.toStream() + stream outputPersonStream = stream from var person in personList.toStream() from var customer in customerList let string newLastName = "Turin" let string newFirstName = "Johnas" @@ -223,7 +223,7 @@ function testQueryExprWithOrderByClauseReturnStream() returns boolean { age: person.age }; - record {| Person value; |}? person = getPersonValue(outputPersonStream.next()); + record {|PersonPos value;|}? person = getPersonValue(outputPersonStream.next()); testPassed = testPassed && person?.value?.firstName == "Johnas" && person?.value?.lastName == "Turin" && person?.value?.age == 30; @@ -246,26 +246,26 @@ function testQueryExprWithOrderByClauseReturnStream() returns boolean { } function testQueryExprWithOrderByClauseAndJoin() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "James", noOfItems: 25}; - Customer c4 = {id: 4, name: "James", noOfItems: 25}; + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerPos c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 3, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 4, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerPos[] customerList = [c1, c2, c3, c4]; + PersonPos[] personList = [p1, p2]; CustomerProfile[] customerProfileList = from var customer in customerList - join var person in personList - on customer.name equals person.lastName - order by customer.noOfItems - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems - }; + join var person in personList + on customer.name equals person.lastName + order by customer.noOfItems + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems + }; return customerProfileList; } @@ -273,14 +273,30 @@ function testQueryExprWithOrderByClauseAndJoin() returns CustomerProfile[] { function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction() returns boolean { boolean testPassed = true; - Employee e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; + Employee e1 = { + name: "Frank", + address: {unitNo: 111, street: "Main Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e2 = { + name: "James", + address: {unitNo: 222, street: "Main Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e3 = { + name: "James", + address: {unitNo: 222, street: "Cross Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e4 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; Employee[] empList = [e1, e2, e3, e4]; @@ -288,10 +304,10 @@ function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction() retur order by emp.address.unitNo descending, emp.address.street.toLowerAscii() select emp; - testPassed = testPassed && opEmpList[0] == empList[2]; - testPassed = testPassed && opEmpList[1] == empList[1]; - testPassed = testPassed && opEmpList[2] == empList[3]; - testPassed = testPassed && opEmpList[3] == empList[0]; + testPassed = testPassed && opEmpList[0] == empList[2]; + testPassed = testPassed && opEmpList[1] == empList[1]; + testPassed = testPassed && opEmpList[2] == empList[3]; + testPassed = testPassed && opEmpList[3] == empList[0]; return testPassed; } @@ -299,16 +315,36 @@ function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction() retur function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction2() returns boolean { boolean testPassed = true; - Employee e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:11, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:111, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1111, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e5 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:1111, two:2, three:3}, - noOfShifts: [3, 2, 3]}; + Employee e1 = { + name: "Frank", + address: {unitNo: 111, street: "Main Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e2 = { + name: "James", + address: {unitNo: 222, street: "Main Street"}, + tokens: {one: 11, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e3 = { + name: "James", + address: {unitNo: 222, street: "Cross Street"}, + tokens: {one: 111, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e4 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 1111, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e5 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 1111, two: 2, three: 3}, + noOfShifts: [3, 2, 3] + }; Employee[] empList = [e1, e2, e3, e4, e5]; @@ -316,36 +352,36 @@ function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction2() retu order by emp.name, emp.tokens["one"] descending, emp.noOfShifts[0] descending select emp; - testPassed = testPassed && opEmpList[0] == empList[4]; - testPassed = testPassed && opEmpList[1] == empList[3]; - testPassed = testPassed && opEmpList[2] == empList[0]; - testPassed = testPassed && opEmpList[3] == empList[2]; - testPassed = testPassed && opEmpList[4] == empList[1]; + testPassed = testPassed && opEmpList[0] == empList[4]; + testPassed = testPassed && opEmpList[1] == empList[3]; + testPassed = testPassed && opEmpList[2] == empList[0]; + testPassed = testPassed && opEmpList[3] == empList[2]; + testPassed = testPassed && opEmpList[4] == empList[1]; return testPassed; } function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction3() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "James", noOfItems: 25}; - Customer c4 = {id: 4, name: "James", noOfItems: 25}; + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerPos c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 3, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 4, name: "James", noOfItems: 25}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c3, c4]; - Person[] personList = [p1, p2]; + CustomerPos[] customerList = [c1, c2, c3, c4]; + PersonPos[] personList = [p1, p2]; CustomerProfile[] customerProfileList = from var customer in customerList - join var person in personList - on customer.name equals person.lastName - order by incrementCount(0), customer.noOfItems descending - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems - }; + join var person in personList + on customer.name equals person.lastName + order by incrementCount(0), customer.noOfItems descending + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems + }; return customerProfileList; } @@ -353,20 +389,56 @@ function testQueryExprWithOrderByClauseHavingUserDefinedOrderKeyFunction3() retu function testQueryExprWithOrderByClauseHavingNaNNilValues() returns boolean { boolean testPassed = true; - Employee e1 = {name: "Frank", address: {unitNo: 111, street: "Main Street"}, tokens: {one:1, two:2, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e2 = {name: "James", address: {unitNo: 222, street: "Main Street"}, tokens: {one:11, two:(), three:3}, - noOfShifts: [1, 2, 3]}; - Employee e3 = {name: "James", address: {unitNo: 222, street: "Cross Street"}, tokens: {one:11, two:(0.0/0.0), - three:3}, noOfShifts: [1, 2, 3]}; - Employee e4 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:3}, - noOfShifts: [1, 2, 3]}; - Employee e5 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:()}, - noOfShifts: [1, 2, 3]}; - Employee e6 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, - three:(0.0/0.0)}, noOfShifts: [1, 2, 3]}; - Employee e7 = {name: "Frank", address: {unitNo: 111, street: "Cross Street"}, tokens: {one:11, two:4, three:55}, - noOfShifts: [1, 2, 3]}; + Employee e1 = { + name: "Frank", + address: {unitNo: 111, street: "Main Street"}, + tokens: {one: 1, two: 2, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e2 = { + name: "James", + address: {unitNo: 222, street: "Main Street"}, + tokens: {one: 11, two: (), three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e3 = { + name: "James", + address: {unitNo: 222, street: "Cross Street"}, + tokens: { + one: 11, + two: (0.0 / 0.0), + three: 3 + }, + noOfShifts: [1, 2, 3] + }; + Employee e4 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 11, two: 4, three: 3}, + noOfShifts: [1, 2, 3] + }; + Employee e5 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 11, two: 4, three: ()}, + noOfShifts: [1, 2, 3] + }; + Employee e6 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: { + one: 11, + two: 4, + three: (0.0 / 0.0) + }, + noOfShifts: [1, 2, 3] + }; + Employee e7 = { + name: "Frank", + address: {unitNo: 111, street: "Cross Street"}, + tokens: {one: 11, two: 4, three: 55}, + noOfShifts: [1, 2, 3] + }; Employee[] empList = [e1, e2, e3, e4, e5, e6, e7]; @@ -374,30 +446,30 @@ function testQueryExprWithOrderByClauseHavingNaNNilValues() returns boolean { order by emp.tokens["two"] descending, emp.tokens["three"] ascending select emp; - testPassed = testPassed && opEmpList[0] == empList[3]; - testPassed = testPassed && opEmpList[1] == empList[6]; - testPassed = testPassed && opEmpList[2] == empList[5]; - testPassed = testPassed && opEmpList[3] == empList[4]; - testPassed = testPassed && opEmpList[4] == empList[0]; - testPassed = testPassed && opEmpList[5] == empList[2]; - testPassed = testPassed && opEmpList[6] == empList[1]; + testPassed = testPassed && opEmpList[0] == empList[3]; + testPassed = testPassed && opEmpList[1] == empList[6]; + testPassed = testPassed && opEmpList[2] == empList[5]; + testPassed = testPassed && opEmpList[3] == empList[4]; + testPassed = testPassed && opEmpList[4] == empList[0]; + testPassed = testPassed && opEmpList[5] == empList[2]; + testPassed = testPassed && opEmpList[6] == empList[1]; return testPassed; } function testQueryExprWithOrderByClauseReturnString() returns string { - Person p1 = {firstName: "Amy", lastName: "Melina", age: 34}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; - Person p3 = {firstName: "Melina", lastName: "Kodel", age: 72}; - Person p4 = {firstName: "Terrence", lastName: "Lewis", age: 19}; - Person p5 = {firstName: "Meghan", lastName: "Markle", age: 55}; + PersonPos p1 = {firstName: "Amy", lastName: "Melina", age: 34}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p3 = {firstName: "Melina", lastName: "Kodel", age: 72}; + PersonPos p4 = {firstName: "Terrence", lastName: "Lewis", age: 19}; + PersonPos p5 = {firstName: "Meghan", lastName: "Markle", age: 55}; - Person[] personList = [p1, p2, p3, p4, p5]; + PersonPos[] personList = [p1, p2, p3, p4, p5]; string outputNameString = from var person in personList - order by person.age descending - limit 3 - select person.firstName+" "+person.lastName+","; + order by person.age descending + limit 3 + select person.firstName + " " + person.lastName + ","; return outputNameString; } @@ -419,53 +491,58 @@ function testQueryExprWithOrderByClauseReturnXML() returns xml { `; xml authors = from var book in bookStore// - order by book.toString() - limit 2 - select book; + order by book.toString() + limit 2 + select book; - return authors; + return authors; } function testQueryExprWithOrderByClauseAndInnerQueries() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerPos c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerPos c5 = {id: 2, name: "James", noOfItems: 30}; - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50}; + PersonPos p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p3 = {firstName: "Zeth", lastName: "James", age: 50}; - Customer[] customerList = [c1, c2, c3, c4, c5]; - Person[] personList = [p1, p2, p3]; + CustomerPos[] customerList = [c1, c2, c3, c4, c5]; + PersonPos[] personList = [p1, p2, p3]; CustomerProfile[] customerProfileList = from var customer in (stream from var c in customerList - order by c.id descending limit 4 select c) - join var person in (from var p in personList order by p.firstName descending limit 2 select p) - on customer.name equals person.lastName - order by incrementCount(0), customer.noOfItems descending - limit 3 - select { - name: person.firstName, - age : person.age, - noOfItems: customer.noOfItems - }; + order by c.id descending + limit 4 + select c) + join var person in (from var p in personList + order by p.firstName descending + limit 2 + select p) + on customer.name equals person.lastName + order by incrementCount(0), customer.noOfItems descending + limit 3 + select { + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems + }; return customerProfileList; } function testQueryExprWithOrderByClauseAndInnerQueries2() returns CustomerProfile[] { - Customer c1 = {id: 1, name: "Melina", noOfItems: 62}; - Customer c2 = {id: 5, name: "James", noOfItems: 5}; - Customer c3 = {id: 9, name: "James", noOfItems: 25}; - Customer c4 = {id: 0, name: "James", noOfItems: 25}; - Customer c5 = {id: 2, name: "James", noOfItems: 30}; - Customer c6 = {id: 3, name: "Melina", noOfItems: 20}; + CustomerPos c1 = {id: 1, name: "Melina", noOfItems: 62}; + CustomerPos c2 = {id: 5, name: "James", noOfItems: 5}; + CustomerPos c3 = {id: 9, name: "James", noOfItems: 25}; + CustomerPos c4 = {id: 0, name: "James", noOfItems: 25}; + CustomerPos c5 = {id: 2, name: "James", noOfItems: 30}; + CustomerPos c6 = {id: 3, name: "Melina", noOfItems: 20}; - Person p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; - Person p3 = {firstName: "Zeth", lastName: "James", age: 50}; + PersonPos p1 = {firstName: "Jennifer", lastName: "Melina", age: 23}; + PersonPos p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonPos p3 = {firstName: "Zeth", lastName: "James", age: 50}; PaymentInfo i1 = {custId: 1, modeOfPayment: "cash"}; PaymentInfo i2 = {custId: 9, modeOfPayment: "debit card"}; @@ -476,8 +553,8 @@ function testQueryExprWithOrderByClauseAndInnerQueries2() returns CustomerProfil PaymentInfo i7 = {custId: 2, modeOfPayment: "cash"}; PaymentInfo i8 = {custId: 3, modeOfPayment: "cash"}; - Customer[] customerList = [c1, c2, c3, c4, c5, c6]; - Person[] personList = [p1, p2, p3]; + CustomerPos[] customerList = [c1, c2, c3, c4, c5, c6]; + PersonPos[] personList = [p1, p2, p3]; PaymentInfo[] paymentList = [i1, i2, i3, i4, i5, i6, i7, i8]; CustomerProfile[] customerProfileList = from var customer in (stream from var c in customerList diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/query-expr-with-query-construct-type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/query-expr-with-query-construct-type.bal index 3cf3bc40fafe..0145356cd7ca 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/query-expr-with-query-construct-type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/query-expr-with-query-construct-type.bal @@ -14,23 +14,23 @@ // specific language governing permissions and limitations // under the License. -type Person record {| +type PersonX record {| string firstName; string lastName; int age; |}; -type Employee record {| +type EmployeeX record {| string firstName; string lastName; string dept; |}; -type Department record { +type DepartmentX record { string dept; }; -type EmpProfile record {| +type EmpProfileX record {| string firstName; string lastName; int age; @@ -38,67 +38,67 @@ type EmpProfile record {| string status; |}; -type PersonValue record {| - Person value; +type PersonValueX record {| + PersonX value; |}; -type EmployeeValue record {| - Employee value; +type EmployeeValueX record {| + EmployeeX value; |}; -type EmpProfileValue record {| - EmpProfile value; +type EmpProfileValueX record {| + EmpProfileX value; |}; -type Customer record {| +type CustomerX record {| readonly int id; readonly string name; int noOfItems; |}; -type CustomerTable table key(id, name); +type CustomerTableX table key(id, name); -type CustomerKeyLessTable table; +type CustomerKeyLessTableX table; -type CustomerValue record {| - Customer value; +type CustomerValueX record {| + CustomerX value; |}; -function getPersonValue((record {| Person value; |}|error?)|(record {| Person value; |}?) returnedVal) -returns PersonValue? { +function getPersonValue((record {|PersonX value;|}|error?)|(record {|PersonX value;|}?) returnedVal) +returns PersonValueX? { var result = returnedVal; - if (result is PersonValue) { + if (result is PersonValueX) { return result; } else { return (); } } -function getEmployeeValue((record {| Employee value; |}|error?)|(record {| Employee value; |}?) returnedVal) -returns EmployeeValue? { +function getEmployeeValue((record {|EmployeeX value;|}|error?)|(record {|EmployeeX value;|}?) returnedVal) +returns EmployeeValueX? { var result = returnedVal; - if (result is EmployeeValue) { + if (result is EmployeeValueX) { return result; } else { return (); } } -function getEmpProfileValue((record {| EmpProfile value; |}|error?)|(record {| EmpProfile value; |}?) returnedVal) -returns EmpProfileValue? { +function getEmpProfileValue((record {|EmpProfileX value;|}|error?)|(record {|EmpProfileX value;|}?) returnedVal) +returns EmpProfileValueX? { var result = returnedVal; - if (result is EmpProfileValue) { + if (result is EmpProfileValueX) { return result; } else { return (); } } -function getCustomer(record {| Customer value; |}? returnedVal) returns Customer? { - if (returnedVal is CustomerValue) { - return returnedVal.value; +function getCustomer(record {|CustomerX value;|}? returnedVal) returns CustomerX? { + if (returnedVal is CustomerValueX) { + return returnedVal.value; } else { - return (); + return (); } } @@ -107,13 +107,13 @@ function getCustomer(record {| Customer value; |}? returnedVal) returns Customer function testSimpleQueryReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonX[] personList = [p1, p2, p3]; - stream outputPersonStream = stream from var person in personList + stream outputPersonStream = stream from var person in personList where person.firstName == "John" let int newAge = 34 select { @@ -122,7 +122,7 @@ function testSimpleQueryReturnStream() returns boolean { age: newAge }; - record {| Person value; |}? person = getPersonValue(outputPersonStream.next()); + record {|PersonX value;|}? person = getPersonValue(outputPersonStream.next()); testPassed = testPassed && person?.value?.firstName == "John" && person?.value?.lastName == "David" && person?.value?.age == 34; @@ -135,11 +135,11 @@ function testSimpleQueryReturnStream() returns boolean { function testSimpleQueryReturnStream2() { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonX[] personList = [p1, p2, p3]; var outputPersonStream = stream from var person in personList where person.firstName == "John" @@ -150,10 +150,10 @@ function testSimpleQueryReturnStream2() { age: newAge }; - assertTrue(outputPersonStream is stream); - stream _ = outputPersonStream; + assertTrue(outputPersonStream is stream); + stream _ = outputPersonStream; - record {| Person value; |}? person = getPersonValue(outputPersonStream.next()); + record {|PersonX value;|}? person = getPersonValue(outputPersonStream.next()); testPassed = testPassed && person?.value?.firstName == "John" && person?.value?.lastName == "David" && person?.value?.age == 34; @@ -163,37 +163,39 @@ function testSimpleQueryReturnStream2() { assertTrue(testPassed); } -type ValueRecord record {| +type ValueRecordX record {| string value; |}; -type TestStream stream; +type TestStreamX stream; -class TestGenerator { - public isolated function next() returns ValueRecord|error? { +class TestGeneratorX { + public isolated function next() returns ValueRecordX|error? { return {value: "Ballerina"}; } } function testSimpleQueryReturnStream3() { - TestGenerator generator = new (); - TestStream testStream = new (generator); + TestGeneratorX generator = new (); + TestStreamX testStream = new (generator); - var outputIntPersonStream = stream from var _ in testStream select 1; + var outputIntPersonStream = stream from var _ in testStream + select 1; assertTrue(outputIntPersonStream is stream); stream _ = outputIntPersonStream; - (record {| int value; |}|error)? x1 = outputIntPersonStream.next(); - if (x1 is record {| int value; |}) { + (record {|int value;|}|error)? x1 = outputIntPersonStream.next(); + if (x1 is record {|int value;|}) { assertEqual(x1.value, 1); } else { assertTrue(false); } - var outputStringPersonStream = stream from var _ in testStream select "ABCD"; + var outputStringPersonStream = stream from var _ in testStream + select "ABCD"; assertTrue(outputStringPersonStream is stream); stream _ = outputStringPersonStream; - (record {| string value; |}|error)? x2 = outputStringPersonStream.next(); - if (x2 is record {| string value; |}) { + (record {|string value;|}|error)? x2 = outputStringPersonStream.next(); + if (x2 is record {|string value;|}) { assertEqual(x2.value, "ABCD"); } else { assertTrue(false); @@ -203,30 +205,30 @@ function testSimpleQueryReturnStream3() { function testStreamInFromClauseWithReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; - - Person[] personList = [p1, p2, p3]; - - stream outputEmployeeStream = stream from var {firstName, lastName, dept} in - >personList.toStream().filter(function (Person person) returns boolean { - return person.firstName == "John"; - }).'map(function (Person person) returns Employee { - Employee employee = { - firstName: person.firstName, - lastName: person.lastName, - dept: "Engineering" - }; - return employee; - }) - select { - firstName: firstName, - lastName: lastName, - dept: dept - }; - - record {| Employee value; |}? employee = getEmployeeValue(outputEmployeeStream.next()); + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p3 = {firstName: "John", lastName: "David", age: 33}; + + PersonX[] personList = [p1, p2, p3]; + + stream outputEmployeeStream = stream from var {firstName, lastName, dept} in + >personList.toStream().filter(function(PersonX person) returns boolean { + return person.firstName == "John"; + }).'map(function(PersonX person) returns EmployeeX { + EmployeeX employee = { + firstName: person.firstName, + lastName: person.lastName, + dept: "Engineering" + }; + return employee; + }) + select { + firstName: firstName, + lastName: lastName, + dept: dept + }; + + record {|EmployeeX value;|}? employee = getEmployeeValue(outputEmployeeStream.next()); testPassed = testPassed && employee?.value?.firstName == "John" && employee?.value?.lastName == "David" && employee?.value?.dept == "Engineering"; @@ -239,33 +241,33 @@ function testStreamInFromClauseWithReturnStream() returns boolean { function testStreamInFromClauseWithReturnStream2() { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonX[] personList = [p1, p2, p3]; var outputEmployeeStream = stream from var {firstName, lastName, dept} in - >personList.toStream().filter(function (Person person) returns boolean { - return person.firstName == "John"; - }).'map(function (Person person) returns Employee { - Employee employee = { - firstName: person.firstName, - lastName: person.lastName, - dept: "Engineering" - }; - return employee; - }) - select { - firstName: firstName, - lastName: lastName, - dept: dept - }; - - assertTrue(outputEmployeeStream is stream); - stream _ = outputEmployeeStream; - - record {| Employee value; |}? employee = getEmployeeValue(outputEmployeeStream.next()); + >personList.toStream().filter(function(PersonX person) returns boolean { + return person.firstName == "John"; + }).'map(function(PersonX person) returns EmployeeX { + EmployeeX employee = { + firstName: person.firstName, + lastName: person.lastName, + dept: "Engineering" + }; + return employee; + }) + select { + firstName: firstName, + lastName: lastName, + dept: dept + }; + + assertTrue(outputEmployeeStream is stream); + stream _ = outputEmployeeStream; + + record {|EmployeeX value;|}? employee = getEmployeeValue(outputEmployeeStream.next()); testPassed = testPassed && employee?.value?.firstName == "John" && employee?.value?.lastName == "David" && employee?.value?.dept == "Engineering"; @@ -277,28 +279,28 @@ function testStreamInFromClauseWithReturnStream2() { function testMultipleFromWhereAndLetReturnStream() returns boolean { boolean testPassed = true; - Employee e1 = {firstName: "John", lastName: "Fonseka", dept: "Engineering"}; - Employee e2 = {firstName: "John", lastName: "David", dept: "HR"}; + EmployeeX e1 = {firstName: "John", lastName: "Fonseka", dept: "Engineering"}; + EmployeeX e2 = {firstName: "John", lastName: "David", dept: "HR"}; - Department d1 = {dept: "Support"}; - Department d2 = {dept: "Dev"}; + DepartmentX d1 = {dept: "Support"}; + DepartmentX d2 = {dept: "Dev"}; - Employee[] employeeList = [e1, e2]; - Department[] departmentList = [d1, d2]; + EmployeeX[] employeeList = [e1, e2]; + DepartmentX[] departmentList = [d1, d2]; - stream outputEmployeeStream = stream from var emp in employeeList - from var department in departmentList - where emp.firstName == "John" - where emp.dept == "Engineering" - let string fname = "Johns" - let string deptName = "Research" - select { - firstName: fname, - lastName: emp.lastName, - dept: deptName - }; + stream outputEmployeeStream = stream from var emp in employeeList + from var department in departmentList + where emp.firstName == "John" + where emp.dept == "Engineering" + let string fname = "Johns" + let string deptName = "Research" + select { + firstName: fname, + lastName: emp.lastName, + dept: deptName + }; - record {| Employee value; |}? employee = getEmployeeValue(outputEmployeeStream.next()); + record {|EmployeeX value;|}? employee = getEmployeeValue(outputEmployeeStream.next()); testPassed = testPassed && employee?.value?.firstName == "Johns" && employee?.value?.lastName == "Fonseka" && employee?.value?.dept == "Research"; @@ -315,31 +317,31 @@ function testMultipleFromWhereAndLetReturnStream() returns boolean { function testMultipleFromWhereAndLetReturnStream2() { boolean testPassed = true; - Employee e1 = {firstName: "John", lastName: "Fonseka", dept: "Engineering"}; - Employee e2 = {firstName: "John", lastName: "David", dept: "HR"}; + EmployeeX e1 = {firstName: "John", lastName: "Fonseka", dept: "Engineering"}; + EmployeeX e2 = {firstName: "John", lastName: "David", dept: "HR"}; - Department d1 = {dept: "Support"}; - Department d2 = {dept: "Dev"}; + DepartmentX d1 = {dept: "Support"}; + DepartmentX d2 = {dept: "Dev"}; - Employee[] employeeList = [e1, e2]; - Department[] departmentList = [d1, d2]; + EmployeeX[] employeeList = [e1, e2]; + DepartmentX[] departmentList = [d1, d2]; var outputEmployeeStream = stream from var emp in employeeList - from var department in departmentList - where emp.firstName == "John" - where emp.dept == "Engineering" - let string fname = "Johns" - let string deptName = "Research" - select { - firstName: fname, - lastName: emp.lastName, - dept: deptName - }; - - assertTrue(outputEmployeeStream is stream); - stream _ = outputEmployeeStream; - - record {| Employee value; |}? employee = getEmployeeValue(outputEmployeeStream.next()); + from var department in departmentList + where emp.firstName == "John" + where emp.dept == "Engineering" + let string fname = "Johns" + let string deptName = "Research" + select { + firstName: fname, + lastName: emp.lastName, + dept: deptName + }; + + assertTrue(outputEmployeeStream is stream); + stream _ = outputEmployeeStream; + + record {|EmployeeX value;|}? employee = getEmployeeValue(outputEmployeeStream.next()); testPassed = testPassed && employee?.value?.firstName == "Johns" && employee?.value?.lastName == "Fonseka" && employee?.value?.dept == "Research"; @@ -352,85 +354,90 @@ function testMultipleFromWhereAndLetReturnStream2() { assertTrue(testPassed); } -type Employee2 record { +type Employee2X record { readonly string name; int salary; }; -type Tbl table key(name); +type TblX table key(name); function testConstructTablesWithRecords() { - table key(name) t = table [ - { name: "John", salary: 100 }, - { name: "Jane", salary: 200 } + table key(name) t = table [ + {name: "John", salary: 100}, + {name: "Jane", salary: 200} ]; - var ct = from Employee2 e in t select e; - assertTrue(ct is table); - table a = ct; + var ct = from Employee2X e in t + select e; + assertTrue(ct is table); + table a = ct; assertEqual(a.toString(), "[{\"name\":\"John\",\"salary\":100},{\"name\":\"Jane\",\"salary\":200}]"); - table key(name) t2 = table [ - { name: "John", salary: 100 }, - { name: "Jane", salary: 200 } - ]; + table key(name) t2 = table [ + {name: "John", salary: 100}, + {name: "Jane", salary: 200} + ]; - var ct2 = from record { readonly string name; int salary; } e in t2 select e; - assertTrue(ct2 is table); - table a2 = ct2; + var ct2 = from record {readonly string name; int salary;} e in t2 + select e; + assertTrue(ct2 is table); + table a2 = ct2; assertEqual(a2.toString(), "[{\"name\":\"John\",\"salary\":100},{\"name\":\"Jane\",\"salary\":200}]"); - var ct3 = from Employee2 e in t select {name: e.name}; + var ct3 = from Employee2X e in t + select {name: e.name}; assertTrue(ct3 is table); table a3 = ct3; assertEqual(a3.toString(), "[{\"name\":\"John\"},{\"name\":\"Jane\"}]"); - Tbl t3 = table [ - { name: "John", salary: 100 }, - { name: "Jane", salary: 200 } + TblX t3 = table [ + {name: "John", salary: 100}, + {name: "Jane", salary: 200} ]; - var ct4 = from Employee2 e in t3 select e; - assertTrue(ct4 is table); - table a4 = ct4; + var ct4 = from Employee2X e in t3 + select e; + assertTrue(ct4 is table); + table a4 = ct4; assertEqual(a4.toString(), "[{\"name\":\"John\",\"salary\":100},{\"name\":\"Jane\",\"salary\":200}]"); } function testConstructMapsWithTuples() { map a = {"a": 1, "b": 2}; - var cm = map from var i in a select ["A",1]; - assertTrue(cm is map); - map cm2 = cm; - assertEqual(cm2, {"A": 1}); + var cm = map from var i in a + select ["A", 1]; + assertTrue(cm is map); + map cm2 = cm; + assertEqual(cm2, {"A": 1}); } function testInnerJoinAndLimitReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Employee e1 = {firstName: "Alex", lastName: "George", dept: "Engineering"}; - Employee e2 = {firstName: "John", lastName: "David", dept: "HR"}; - Employee e3 = {firstName: "Ranjan", lastName: "Fonseka", dept: "Operations"}; + EmployeeX e1 = {firstName: "Alex", lastName: "George", dept: "Engineering"}; + EmployeeX e2 = {firstName: "John", lastName: "David", dept: "HR"}; + EmployeeX e3 = {firstName: "Ranjan", lastName: "Fonseka", dept: "Operations"}; - Person[] personList = [p1, p2]; - Employee[] employeeList = [e1, e2, e3]; + PersonX[] personList = [p1, p2]; + EmployeeX[] employeeList = [e1, e2, e3]; - stream outputEmpProfileStream = stream from var person in personList.toStream() - join Employee employee in employeeList.toStream() + stream outputEmpProfileStream = stream from var person in personList.toStream() + join EmployeeX employee in employeeList.toStream() on person.firstName equals employee.firstName - limit 1 - select { - firstName: employee.firstName, - lastName: employee.lastName, - age: person.age, - dept: employee.dept, - status: "Permanent" - }; + limit 1 + select { + firstName: employee.firstName, + lastName: employee.lastName, + age: person.age, + dept: employee.dept, + status: "Permanent" + }; - record {| EmpProfile value; |}? empProfile = getEmpProfileValue(outputEmpProfileStream.next()); + record {|EmpProfileX value;|}? empProfile = getEmpProfileValue(outputEmpProfileStream.next()); testPassed = testPassed && empProfile?.value?.firstName == "Alex" && empProfile?.value?.lastName == "George" && empProfile?.value?.age == 23 && empProfile?.value?.dept == "Engineering" && empProfile?.value?.status == "Permanent"; @@ -444,32 +451,32 @@ function testInnerJoinAndLimitReturnStream() returns boolean { function testInnerJoinAndLimitReturnStream2() { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonX p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonX p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Employee e1 = {firstName: "Alex", lastName: "George", dept: "Engineering"}; - Employee e2 = {firstName: "John", lastName: "David", dept: "HR"}; - Employee e3 = {firstName: "Ranjan", lastName: "Fonseka", dept: "Operations"}; + EmployeeX e1 = {firstName: "Alex", lastName: "George", dept: "Engineering"}; + EmployeeX e2 = {firstName: "John", lastName: "David", dept: "HR"}; + EmployeeX e3 = {firstName: "Ranjan", lastName: "Fonseka", dept: "Operations"}; - Person[] personList = [p1, p2]; - Employee[] employeeList = [e1, e2, e3]; + PersonX[] personList = [p1, p2]; + EmployeeX[] employeeList = [e1, e2, e3]; var outputEmpProfileStream = stream from var person in personList.toStream() - join Employee employee in employeeList.toStream() + join EmployeeX employee in employeeList.toStream() on person.firstName equals employee.firstName - limit 1 - select { - firstName: employee.firstName, - lastName: employee.lastName, - age: person.age, - dept: employee.dept, - status: "Permanent" - }; + limit 1 + select { + firstName: employee.firstName, + lastName: employee.lastName, + age: person.age, + dept: employee.dept, + status: "Permanent" + }; - assertTrue(outputEmpProfileStream is stream); - stream _ = outputEmpProfileStream; + assertTrue(outputEmpProfileStream is stream); + stream _ = outputEmpProfileStream; - record {| EmpProfile value; |}? empProfile = getEmpProfileValue(outputEmpProfileStream.next()); + record {|EmpProfileX value;|}? empProfile = getEmpProfileValue(outputEmpProfileStream.next()); testPassed = testPassed && empProfile?.value?.firstName == "Alex" && empProfile?.value?.lastName == "George" && empProfile?.value?.age == 23 && empProfile?.value?.dept == "Engineering" && empProfile?.value?.status == "Permanent"; @@ -484,22 +491,22 @@ function testInnerJoinAndLimitReturnStream2() { function testSimpleQueryExprReturnTable() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; - CustomerTable customerTable = table key(id, name) from var customer in customerList + CustomerTableX customerTable = table key(id, name) from var customer in customerList select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems }; - if (customerTable is CustomerTable) { + if (customerTable is CustomerTableX) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerX? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[0]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[1]; @@ -515,11 +522,11 @@ function testSimpleQueryExprReturnTable() returns boolean { function testSimpleQueryExprReturnTable2() { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; var customerTable = table key(id, name) from var customer in customerList select { @@ -528,12 +535,12 @@ function testSimpleQueryExprReturnTable2() { noOfItems: customer.noOfItems }; - assertTrue(customerTable is CustomerTable); - CustomerTable _ = customerTable; + assertTrue(customerTable is CustomerTableX); + CustomerTableX _ = customerTable; - if (customerTable is CustomerTable) { + if (customerTable is CustomerTableX) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerX? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[0]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[1]; @@ -547,26 +554,29 @@ function testSimpleQueryExprReturnTable2() { } function testTableWithDuplicateKeys() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Customer[] customerList = [c1, c2, c1]; + CustomerX[] customerList = [c1, c2, c1]; - CustomerTable customerTable = table key(id, name) from var customer in customerList + CustomerTableX customerTable = table key(id, name) from var customer in customerList select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems }; - assertEqual(customerTable, table key(id,name) [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5}]); + assertEqual(customerTable, table key(id, name) [ + {"id": 1, "name": "Melina", "noOfItems": 12}, + {"id": 2, "name": "James", "noOfItems": 5} + ]); } function testTableWithDuplicateKeys2() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Customer[] customerList = [c1, c2, c1]; + CustomerX[] customerList = [c1, c2, c1]; var customerTable = table key(id, name) from var customer in customerList select { @@ -575,23 +585,26 @@ function testTableWithDuplicateKeys2() { noOfItems: customer.noOfItems }; - assertTrue(customerTable is CustomerTable); - CustomerTable _ = customerTable; + assertTrue(customerTable is CustomerTableX); + CustomerTableX _ = customerTable; - assertEqual(customerTable, table key(id,name) [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5}]); + assertEqual(customerTable, table key(id, name) [ + {"id": 1, "name": "Melina", "noOfItems": 12}, + {"id": 2, "name": "James", "noOfItems": 5} + ]); } function testTableNoDuplicatesAndOnConflictReturnTable() returns boolean { boolean testPassed = true; error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; - CustomerTable|error customerTable = table key(id, name) from var customer in customerList + CustomerTableX|error customerTable = table key(id, name) from var customer in customerList select { id: customer.id, name: customer.name, @@ -599,9 +612,9 @@ function testTableNoDuplicatesAndOnConflictReturnTable() returns boolean { } on conflict onConflictError; - if (customerTable is CustomerTable) { + if (customerTable is CustomerTableX) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerX? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[0]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[1]; @@ -615,113 +628,113 @@ function testTableNoDuplicatesAndOnConflictReturnTable() returns boolean { } function testTableWithDuplicatesAndOnConflictReturnTable() { - error onConflictError = error("Key Conflict", message = "cannot insert."); + error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Customer[] customerList = [c1, c2, c1]; + CustomerX[] customerList = [c1, c2, c1]; - CustomerTable|error customerTable = table key(id, name) from var customer in customerList - select { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - } - on conflict onConflictError; + CustomerTableX|error customerTable = table key(id, name) from var customer in customerList + select { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + } + on conflict onConflictError; - validateKeyConflictError(customerTable); + validateKeyConflictError(customerTable); } function testQueryExprWithOtherClausesReturnTable() { - error onConflictError = error("Key Conflict", message = "cannot insert."); + error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonX p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonX p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c1]; - Person[] personList = [p1, p2]; + CustomerX[] customerList = [c1, c2, c1]; + PersonX[] personList = [p1, p2]; - CustomerTable|error customerTable = table key(id, name) from var customer in customerList - from var person in personList - let int items = 25 - let string customerName = "Bini" - where customer.id == 1 - where person.firstName == "Amy" - select { - id: customer.id, - name: customerName, - noOfItems: items - } - on conflict onConflictError; + CustomerTableX|error customerTable = table key(id, name) from var customer in customerList + from var person in personList + let int items = 25 + let string customerName = "Bini" + where customer.id == 1 + where person.firstName == "Amy" + select { + id: customer.id, + name: customerName, + noOfItems: items + } + on conflict onConflictError; - validateKeyConflictError(customerTable); + validateKeyConflictError(customerTable); } function validateKeyConflictError(any|error value) { - if (value is error) { - any|error detailMessage = value.detail()["message"]; - if (value.message() == "Key Conflict" + if (value is error) { + any|error detailMessage = value.detail()["message"]; + if (value.message() == "Key Conflict" && detailMessage is string && detailMessage == "cannot insert.") { - return; - } - panic error("Assertion error"); - } - panic error("Expected error, found: " + (typeof value).toString()); + return; + } + panic error("Assertion error"); + } + panic error("Expected error, found: " + (typeof value).toString()); } function testQueryExprWithJoinClauseReturnTable() { - error onConflictError = error("Key Conflict", message = "cannot insert."); + error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonX p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonX p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList = [c1, c2, c1]; - Person[] personList = [p1, p2]; + CustomerX[] customerList = [c1, c2, c1]; + PersonX[] personList = [p1, p2]; - CustomerTable|error customerTable = table key(id, name) from var customer in customerList - join var person in personList - on customer.name equals person.lastName - select { - id: customer.id, - name: person.firstName, - noOfItems: customer.noOfItems - } - on conflict onConflictError; + CustomerTableX|error customerTable = table key(id, name) from var customer in customerList + join var person in personList + on customer.name equals person.lastName + select { + id: customer.id, + name: person.firstName, + noOfItems: customer.noOfItems + } + on conflict onConflictError; - validateKeyConflictError(customerTable); + validateKeyConflictError(customerTable); } function testQueryExprWithLimitClauseReturnTable() returns boolean { - boolean testPassed = true; - error onConflictError = error("Key Conflict", message = "cannot insert."); - - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Melina", noOfItems: 25}; - - Customer[] customerList = [c1, c2, c3]; - - CustomerTable|error customerTable = table key(id, name) from var customer in customerList.toStream() - where customer.name == "Melina" - limit 1 - select { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - } - on conflict onConflictError; - - if (customerTable is CustomerTable) { + boolean testPassed = true; + error onConflictError = error("Key Conflict", message = "cannot insert."); + + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Melina", noOfItems: 25}; + + CustomerX[] customerList = [c1, c2, c3]; + + CustomerTableX|error customerTable = table key(id, name) from var customer in customerList.toStream() + where customer.name == "Melina" + limit 1 + select { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + } + on conflict onConflictError; + + if (customerTable is CustomerTableX) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerX? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[0]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == (); @@ -733,22 +746,22 @@ function testQueryExprWithLimitClauseReturnTable() returns boolean { function testKeyLessTableWithReturnTable() returns boolean { boolean testPassed = true; - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; - CustomerKeyLessTable customerTable = table key(id, name) from var customer in customerList + CustomerKeyLessTableX customerTable = table key(id, name) from var customer in customerList select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems }; - if (customerTable is CustomerKeyLessTable) { + if (customerTable is CustomerKeyLessTableX) { var itr = customerTable.iterator(); - Customer? customer = getCustomer(itr.next()); + CustomerX? customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[0]; customer = getCustomer(itr.next()); testPassed = testPassed && customer == customerList[1]; @@ -761,7 +774,7 @@ function testKeyLessTableWithReturnTable() returns boolean { return testPassed; } -type User record { +type UserX record { readonly int id; string firstName; string lastName; @@ -769,67 +782,67 @@ type User record { }; function testQueryConstructingTableUpdateKeyPanic1() returns error? { - table key(id) users = table [ + table key(id) users = table [ {id: 1, firstName: "John", lastName: "Doe", age: 25} ]; var result = table key(id, name) from var user in users - where user.age > 21 && user.age < 60 - select {id: user.id, name: user.firstName, user}; + where user.age > 21 && user.age < 60 + select {id: user.id, name: user.firstName, user}; var r2 = result[1, "John"]; + UserX user; + }>result[1, "John"]; r2.id = 1; } -type NewUser record {| +type NewUserX record {| readonly int id; readonly string name; - User user; + UserX user; |}; function testQueryConstructingTableUpdateKeyPanic2() returns error? { - table key(id) users = table [ + table key(id) users = table [ {id: 1, firstName: "John", lastName: "Doe", age: 25} ]; - table key(id, name) result = - table key(id, name) from var user in users - where user.age > 21 && user.age < 60 - select {id: user.id, name: user.firstName, user}; + table key(id, name) result = + table key(id, name) from var user in users + where user.age > 21 && user.age < 60 + select {id: user.id, name: user.firstName, user}; var r2 = result[1, "John"]; + UserX user; + }>result[1, "John"]; r2.id = 2; } -type CustomErrorDetail record {| +type CustomErrorDetailX record {| string message; int code; |}; -type CustomError error; +type CustomErrorX error; function testTableOnConflict() { error? onConflictError1 = error("Key Conflict", message = "cannot insert."); error|null onConflictError2 = (); error|null onConflictError3 = null; - CustomError? onConflictError4 = error ("error msg 1", message = "error 1", code = 500); - CustomError? onConflictError5 = error ("error msg 2", message = "error 2", code = 500); + CustomErrorX? onConflictError4 = error("error msg 1", message = "error 1", code = 500); + CustomErrorX? onConflictError5 = error("error msg 2", message = "error 2", code = 500); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 1, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 1, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; var customerTable1 = table key(id) from var customer in customerList select { @@ -849,7 +862,10 @@ function testTableOnConflict() { } on conflict onConflictError2; - assertEqual(customerTable2, table key(id) [{"id":1,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}]); + assertEqual(customerTable2, table key(id) [ + {"id": 1, "name": "James", "noOfItems": 5}, + {"id": 3, "name": "Anne", "noOfItems": 20} + ]); var customerTable3 = table key(id) from var customer in customerList select { @@ -859,7 +875,10 @@ function testTableOnConflict() { } on conflict onConflictError3; - assertEqual(customerTable3, table key(id) [{"id":1,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}]); + assertEqual(customerTable3, table key(id) [ + {"id": 1, "name": "James", "noOfItems": 5}, + {"id": 3, "name": "Anne", "noOfItems": 20} + ]); var customerTable4 = table key(id) from var customer in customerList select { @@ -889,7 +908,10 @@ function testTableOnConflict() { } on conflict null; - assertEqual(customerTable6, table key(id) [{"id":1,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}]); + assertEqual(customerTable6, table key(id) [ + {"id": 1, "name": "James", "noOfItems": 5}, + {"id": 3, "name": "Anne", "noOfItems": 20} + ]); var customerTable7 = table key(id) from var customer in customerList select { @@ -899,7 +921,10 @@ function testTableOnConflict() { } on conflict (); - assertEqual(customerTable7, table key(id) [{"id":1,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}]); + assertEqual(customerTable7, table key(id) [ + {"id": 1, "name": "James", "noOfItems": 5}, + {"id": 3, "name": "Anne", "noOfItems": 20} + ]); } type Token record {| @@ -911,7 +936,8 @@ type TokenTable table key(idx); function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInLetClause() { TokenTable|error tbl1 = table key(idx) from int i in 1 ... 3 - let int[] arr = from var j in 1 ... 3 select j + let int[] arr = from var j in 1 ... 3 + select j select { idx: arr[i - 1], value: "A" + i.toString() @@ -919,10 +945,10 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInLetC on conflict error("Duplicate Key"); TokenTable expectedTbl = table [ - {"idx": 1, "value": "A1"}, - {"idx": 2, "value": "A2"}, - {"idx": 3, "value": "A3"} - ]; + {"idx": 1, "value": "A1"}, + {"idx": 2, "value": "A2"}, + {"idx": 3, "value": "A3"} + ]; assertEqual(true, tbl1 is TokenTable); if tbl1 is TokenTable { @@ -930,7 +956,8 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInLetC } TokenTable|error tbl2 = table key(idx) from int i in [1, 2, 1] - let int[] arr = from var j in 1 ... 3 select j + let int[] arr = from var j in 1 ... 3 + select j select { idx: arr[i], value: "A" + i.toString() @@ -946,7 +973,8 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInLetC function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInWhereClause() { TokenTable|error tbl1 = table key(idx) from int i in 1 ... 3 let int[] arr = [1, 2, 3] - where arr == from int j in 1...3 select j + where arr == from int j in 1 ... 3 + select j select { idx: i, value: "A" + i.toString() @@ -954,10 +982,10 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInWher on conflict error("Duplicate Key"); TokenTable expectedTbl = table [ - {"idx": 1, "value": "A1"}, - {"idx": 2, "value": "A2"}, - {"idx": 3, "value": "A3"} - ]; + {"idx": 1, "value": "A1"}, + {"idx": 2, "value": "A2"}, + {"idx": 3, "value": "A3"} + ]; assertEqual(true, tbl1 is TokenTable); if tbl1 is TokenTable { @@ -966,7 +994,8 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInWher TokenTable|error tbl2 = table key(idx) from int i in [1, 2, 1] let int[] arr = [1, 2, 3] - where arr == from int j in 1...3 select j + where arr == from int j in 1 ... 3 + select j select { idx: i, value: "A" + i.toString() @@ -980,7 +1009,7 @@ function testQueryConstructingTableWithOnConflictClauseHavingNonTableQueryInWher } function testQueryConstructingTableWithOnConflictsWithVarRef() { - TokenTable|error tbl1 = table key(idx) from int i in [1, 2, 3, 1, 2, 3] + TokenTable|error tbl1 = table key(idx) from int i in [1, 2, 3, 1, 2, 3] let string value = "A" + i.toString() select { idx: i, @@ -1004,13 +1033,13 @@ function testQueryConstructingTableWithOnConflictsWithVarRef() { } function testMapConstructingQueryExpr() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] list1 = [c1, c2, c3]; + CustomerX[] list1 = [c1, c2, c3]; - map map1 = map from var customer in list1 + map map1 = map from var customer in list1 select [customer.id.toString(), customer]; assertEqual(map1, {"1": {id: 1, name: "Melina", noOfItems: 12}, "2": {id: 2, name: "James", noOfItems: 5}, "3": {id: 3, name: "Anne", noOfItems: 20}}); @@ -1045,37 +1074,37 @@ function testMapConstructingQueryExpr() { map map4 = map from var item in list4 select [item[0], item[1]]; - map expectedMap = {"a":123,"b":123,"c":error("Error"),"zero":0}; + map expectedMap = {"a": 123, "b": 123, "c": error("Error"), "zero": 0}; - assertEqual(expectedMap.length(), (> map4).length()); + assertEqual(expectedMap.length(), (>map4).length()); foreach var key in expectedMap.keys() { - assertEqual(expectedMap[key], (> map4)[key]); + assertEqual(expectedMap[key], (>map4)[key]); } } function testMapConstructingQueryExpr2() { map map1 = map from var e in map from var e in [1, 2, 10, 3, 5, 20] - order by e descending - select [e.toString(), e] - order by e ascending - select [e.toString(), e]; - assertEqual(map1, {"1":1,"2":2,"3":3,"5":5,"10":10,"20":20}); + order by e descending + select [e.toString(), e] + order by e ascending + select [e.toString(), e]; + assertEqual(map1, {"1": 1, "2": 2, "3": 3, "5": 5, "10": 10, "20": 20}); map map2 = map from var e in (from var e in [1, 2, 5, 4] - let int f = e / 2 - order by f ascending - select f) - order by e descending - select [e.toString(), e]; - assertEqual(map2, {"2":2,"1":1,"0":0}); + let int f = e / 2 + order by f ascending + select f) + order by e descending + select [e.toString(), e]; + assertEqual(map2, {"2": 2, "1": 1, "0": 0}); } function testMapConstructingQueryExprWithDuplicateKeys() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] list1 = [c1, c2, c3, c1, c2, c3]; + CustomerX[] list1 = [c1, c2, c3, c1, c2, c3]; var map1 = map from var customer in list1 select [customer.id.toString(), customer]; @@ -1104,12 +1133,12 @@ function testMapConstructingQueryExprWithDuplicateKeys() { } function testMapConstructingQueryExprWithOnConflict() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; ()|error conflictMsg1 = (); - Customer[] list1 = [c1, c2, c3, c1, c2, c3]; + CustomerX[] list1 = [c1, c2, c3, c1, c2, c3]; var mapWithOnConflict1 = map from var customer in list1 select [customer.id.toString(), customer] @@ -1136,7 +1165,7 @@ function testMapConstructingQueryExprWithOnConflict() { [string:Char, int:Signed16] t5 = ["b", 123]; [string, int] t6 = ["c", -123]; [string, int:Unsigned32] t7 = ["zero", 0]; - CustomError? onConflictError4 = error("error msg 1", message = "Error 2", code = 500); + CustomErrorX? onConflictError4 = error("error msg 1", message = "Error 2", code = 500); [string, int][] list3 = [t4, t5, t4, t6, t6, t7, t7, t4]; map|error map3 = map from var item in list3 @@ -1155,14 +1184,14 @@ function testMapConstructingQueryExprWithOnConflict() { function testMapConstructingQueryExprWithOtherClauses() { error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonX p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonX p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList1 = [c1, c2, c2]; - Person[] personList = [p1, p2]; + CustomerX[] customerList1 = [c1, c2, c2]; + PersonX[] personList = [p1, p2]; var selectedCustomers1 = map from var customer in customerList1 from var person in personList @@ -1180,14 +1209,14 @@ function testMapConstructingQueryExprWithOtherClauses() { on conflict onConflictError; assertEqual(selectedCustomers1, {"Amy Melina": {"id": 1, "name": "Amy Melina"}}); - Customer c3 = {id: 1, name: "Melina", noOfItems: 22}; - Customer c4 = {id: 2, name: "James", noOfItems: 15}; - Customer c5 = {id: 1, name: "Melina", noOfItems: 10}; - Customer c6 = {id: 2, name: "James", noOfItems: 11}; + CustomerX c3 = {id: 1, name: "Melina", noOfItems: 22}; + CustomerX c4 = {id: 2, name: "James", noOfItems: 15}; + CustomerX c5 = {id: 1, name: "Melina", noOfItems: 10}; + CustomerX c6 = {id: 2, name: "James", noOfItems: 11}; - Customer[] customerList2 = [c1, c2, c3, c4, c5, c6]; + CustomerX[] customerList2 = [c1, c2, c3, c4, c5, c6]; - map|error selectedCustomers2 = map from var customer in customerList2 + map|error selectedCustomers2 = map from var customer in customerList2 from var person in personList let string fullName = person.firstName + " " + person.lastName where customer.name == person.lastName @@ -1224,195 +1253,218 @@ function testMapConstructingQueryExprWithOtherClauses() { function testMapConstructingQueryExprWithJoinClause() { error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; - Person p1 = {firstName: "Amy", lastName: "Melina", age: 23}; - Person p2 = {firstName: "Frank", lastName: "James", age: 30}; + PersonX p1 = {firstName: "Amy", lastName: "Melina", age: 23}; + PersonX p2 = {firstName: "Frank", lastName: "James", age: 30}; - Customer[] customerList1 = [c1, c2, c1]; - Person[] personList = [p1, p2]; + CustomerX[] customerList1 = [c1, c2, c1]; + PersonX[] personList = [p1, p2]; var customerMap1 = map from var customer in customerList1 join var person in personList on customer.name equals person.lastName - select [customer.id.toString(), { - id: customer.id, - name: person.firstName, - age: person.age, - noOfItems: customer.noOfItems - }] + select [ + customer.id.toString(), + { + id: customer.id, + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems + } + ] on conflict onConflictError; assertEqual(customerMap1, onConflictError); - Customer[] customerList2 = [c1, c2]; + CustomerX[] customerList2 = [c1, c2]; var customerMap2 = map from var customer in customerList2 join var person in personList on customer.name equals person.lastName - select [customer.id.toString(), { - id: customer.id, - name: person.firstName, - age: person.age, - noOfItems: customer.noOfItems - }] + select [ + customer.id.toString(), + { + id: customer.id, + name: person.firstName, + age: person.age, + noOfItems: customer.noOfItems + } + ] on conflict onConflictError; - assertEqual(customerMap2, {"1":{"id":1,"name":"Amy","age":23,"noOfItems":12},"2":{"id":2,"name":"Frank","age":30,"noOfItems":5}}); + assertEqual(customerMap2, {"1": {"id": 1, "name": "Amy", "age": 23, "noOfItems": 12}, "2": {"id": 2, "name": "Frank", "age": 30, "noOfItems": 5}}); } function testMapConstructingQueryExprWithLimitClause() { error onConflictError = error("Key Conflict", message = "cannot insert."); - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Melina", noOfItems: 25}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Melina", noOfItems: 25}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; - map|error customerMap1 = map from var customer in customerList.toStream() + map|error customerMap1 = map from var customer in customerList.toStream() where customer.name == "Melina" limit 1 - select [customer.name, { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - }] + select [ + customer.name, + { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + } + ] on conflict onConflictError; - assertEqual(customerMap1, {"Melina":{"id":1,"name":"Melina","noOfItems":12}}); + assertEqual(customerMap1, {"Melina": {"id": 1, "name": "Melina", "noOfItems": 12}}); } function testMapConstructingQueryExprWithOrderByClause() { map sorted1 = map from var e in [1, 2, 10, 3, 5, 20] - order by e ascending - select [e.toString(), e]; - assertEqual(sorted1, {"1":1,"2":2,"3":3,"5":5,"10":10,"20":20}); + order by e ascending + select [e.toString(), e]; + assertEqual(sorted1, {"1": 1, "2": 2, "3": 3, "5": 5, "10": 10, "20": 20}); map sorted2 = map from var e in [1, 2, 10, 3, 5, 20] - order by e descending - select [e.toString(), e]; - assertEqual(sorted2, {"20":20,"10":10,"5":5,"3":3,"2":2,"1":1}); + order by e descending + select [e.toString(), e]; + assertEqual(sorted2, {"20": 20, "10": 10, "5": 5, "3": 3, "2": 2, "1": 1}); var sorted3 = map from var e in ["1", "2", "10", "3", "5", "20"] - order by e ascending - select [e, e] on conflict (); - assertEqual(sorted3, {"1":"1","10":"10","2":"2","20":"20","3":"3","5":"5"}); + order by e ascending + select [e, e] + on conflict (); + assertEqual(sorted3, {"1": "1", "10": "10", "2": "2", "20": "20", "3": "3", "5": "5"}); var sorted4 = map from var e in [1, 2, 5, 4] - let int f = e / 2 - order by f ascending - select [f.toString(), e] on conflict error("Error"); + let int f = e / 2 + order by f ascending + select [f.toString(), e] + on conflict error("Error"); assertEqual(sorted4, error("Error")); } type Error error; + type Json json; + type IntOrString int|string; + type ZeroOrOne 1|0; type MapOfJsonOrError map|Error; + type MapOfIntOrError map|Error; + type ErrorOrMapOfZeroOrOne Error|map; function testMapConstructingQueryExprWithReferenceTypes() { map sorted1 = map from var e in [1, 0, 1, 0, 1, 1] - order by e ascending - select [e.toString(), e]; - assertEqual(sorted1, {"0":0,"1":1}); + order by e ascending + select [e.toString(), e]; + assertEqual(sorted1, {"0": 0, "1": 1}); ErrorOrMapOfZeroOrOne sorted2 = map from var e in [1, 0, 1, 0, 0, 0] - order by e ascending - select [e.toString(), e]; - assertEqual(sorted2, {"0":0,"1":1}); + order by e ascending + select [e.toString(), e]; + assertEqual(sorted2, {"0": 0, "1": 1}); map sorted3 = map from var e in ["1", "2", "10", "3", "5", "20"] - order by e ascending - select [e, e] on conflict (); - assertEqual(sorted3, {"1":"1","10":"10","2":"2","20":"20","3":"3","5":"5"}); + order by e ascending + select [e, e] + on conflict (); + assertEqual(sorted3, {"1": "1", "10": "10", "2": "2", "20": "20", "3": "3", "5": "5"}); MapOfJsonOrError sorted4 = map from var e in ["1", "2", "10", "3", "5", "20"] - order by e ascending - select [e, e] on conflict error("Error"); - assertEqual(sorted4, {"1":"1","10":"10","2":"2","20":"20","3":"3","5":"5"}); + order by e ascending + select [e, e] + on conflict error("Error"); + assertEqual(sorted4, {"1": "1", "10": "10", "2": "2", "20": "20", "3": "3", "5": "5"}); MapOfIntOrError sorted5 = map from var e in [1, 2, 5, 4] - let int f = e / 2 - order by f ascending - select [f.toString(), e] on conflict error("Error"); + let int f = e / 2 + order by f ascending + select [f.toString(), e] + on conflict error("Error"); assertEqual(sorted5, error("Error")); } function testReadonlyTable() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList1 = [c1, c2, c3]; + CustomerX[] customerList1 = [c1, c2, c3]; - CustomerKeyLessTable & readonly customerTable1 = table key(id, name) from var customer in customerList1 + CustomerKeyLessTableX & readonly customerTable1 = table key(id, name) from var customer in customerList1 select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems }; - any _ = customerTable1; - assertEqual((typeof(customerTable1)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); - assertEqual(customerTable1.toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + any _ = customerTable1; + assertEqual((typeof (customerTable1)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); + assertEqual(customerTable1.toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); - Customer[] & readonly customerList2 = [c1, c2, c3].cloneReadOnly(); + CustomerX[] & readonly customerList2 = [c1, c2, c3].cloneReadOnly(); - CustomerKeyLessTable & readonly|error customerTable2 = table key(id, name) from var customer in customerList2 + CustomerKeyLessTableX & readonly|error customerTable2 = table key(id, name) from var customer in customerList2 select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems - } on conflict error("Error"); - any _ = (checkpanic customerTable2); - assertEqual((typeof(checkpanic customerTable2)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); - assertEqual((checkpanic customerTable2).toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + } + on conflict error("Error"); + any _ = (checkpanic customerTable2); + assertEqual((typeof (checkpanic customerTable2)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); + assertEqual((checkpanic customerTable2).toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); - CustomerKeyLessTable customerTable3 = table key(id, name) from var customer in customerList2 + CustomerKeyLessTableX customerTable3 = table key(id, name) from var customer in customerList2 select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems - } on conflict (); - assertEqual((typeof(customerTable3)).toString(), "typedesc table key(id, name)"); - assertEqual(customerTable3.toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + } + on conflict (); + assertEqual((typeof (customerTable3)).toString(), "typedesc table key(id, name)"); + assertEqual(customerTable3.toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); } function testReadonlyTable2() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList1 = [c1, c2, c3]; + CustomerX[] customerList1 = [c1, c2, c3]; - CustomerTable & readonly customerTable1 = table key(id, name) from var customer in customerList1 + CustomerTableX & readonly customerTable1 = table key(id, name) from var customer in customerList1 select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems }; - any _ = customerTable1; + any _ = customerTable1; assertEqual((typeof customerTable1).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); - assertEqual(customerTable1.toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + assertEqual(customerTable1.toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); - Customer[] customerList2 = [c1, c2, c3]; + CustomerX[] customerList2 = [c1, c2, c3]; - CustomerTable & readonly|error customerTable2 = table key(id, name) from var customer in customerList2 - select customer.cloneReadOnly() on conflict error("Error"); - any _ = (checkpanic customerTable2); - assertEqual((typeof(checkpanic customerTable2)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); - assertEqual((checkpanic customerTable2).toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + CustomerTableX & readonly|error customerTable2 = table key(id, name) from var customer in customerList2 + select customer.cloneReadOnly() + on conflict error("Error"); + any _ = (checkpanic customerTable2); + assertEqual((typeof (checkpanic customerTable2)).toString(), "typedesc [{\"id\":1,\"name\":\"Melina\",\"noOfItems\":12},{\"id\":2,\"name\":\"James\",\"noOfItems\":5},{\"id\":3,\"name\":\"Anne\",\"noOfItems\":20}]"); + assertEqual((checkpanic customerTable2).toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); - CustomerTable customerTable3 = table key(id, name) from var customer in customerList2 + CustomerTableX customerTable3 = table key(id, name) from var customer in customerList2 select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems - } on conflict (); - assertEqual((typeof(customerTable3)).toString(), "typedesc table key(id, name)"); - assertEqual(customerTable3.toString(), [{"id":1,"name":"Melina","noOfItems":12},{"id":2,"name":"James","noOfItems":5},{"id":3,"name":"Anne","noOfItems":20}].toString()); + } + on conflict (); + assertEqual((typeof (customerTable3)).toString(), "typedesc table key(id, name)"); + assertEqual(customerTable3.toString(), [{"id": 1, "name": "Melina", "noOfItems": 12}, {"id": 2, "name": "James", "noOfItems": 5}, {"id": 3, "name": "Anne", "noOfItems": 20}].toString()); } type IdRec record {| @@ -1420,76 +1472,89 @@ type IdRec record {| |}; function testReadonlyTable3() { - table key(id) & readonly|error tbl = table key(id) from var i in [1, 2, 3, 4, 2, 3] - select { - id: i - } on conflict (); - - assertEqual((typeof(tbl)).toString(), "typedesc [{\"id\":1},{\"id\":2},{\"id\":3},{\"id\":4}]"); - assertEqual(tbl, table key(id) [{"id":1},{"id":2},{"id":3},{"id":4}]); + table key(id) & readonly|error tbl = table key(id) from var i in [1, 2, 3, 4, 2, 3] + select { + id: i + } + on conflict (); - if tbl !is error { - IdRec? member1 = tbl[1]; - assertEqual(member1, {"id":1}); - } + assertEqual((typeof (tbl)).toString(), "typedesc [{\"id\":1},{\"id\":2},{\"id\":3},{\"id\":4}]"); + assertEqual(tbl, table key(id) [ + {"id": 1}, + {"id": 2}, + {"id": 3}, + {"id": 4} + ]); + + if tbl !is error { + IdRec? member1 = tbl[1]; + assertEqual(member1, {"id": 1}); + } - table & readonly tbl2 = table key() from var i in [1, 2, 3, 4, 2, 3] - select { - id: i - }; + table & readonly tbl2 = table key() from var i in [1, 2, 3, 4, 2, 3] + select { + id: i + }; - assertEqual((typeof(tbl2)).toString(), "typedesc [{\"id\":1},{\"id\":2},{\"id\":3},{\"id\":4},{\"id\":2},{\"id\":3}]"); - assertEqual(tbl2, table key() [{"id":1},{"id":2},{"id":3},{"id":4},{"id":2},{"id":3}]); + assertEqual((typeof (tbl2)).toString(), "typedesc [{\"id\":1},{\"id\":2},{\"id\":3},{\"id\":4},{\"id\":2},{\"id\":3}]"); + assertEqual(tbl2, table key() [ + {"id": 1}, + {"id": 2}, + {"id": 3}, + {"id": 4}, + {"id": 2}, + {"id": 3} + ]); } function testConstructingListOfTablesUsingQueryWithReadonly() { - table key(id) & readonly users1 = table [ + table key(id) & readonly users1 = table [ {id: 1, firstName: "John", lastName: "Doe", age: 25} ]; - (table key(id))[] uList = [users1]; + (table key(id))[] uList = [users1]; - (table key(id))[] & readonly result = from var user in uList - select user.cloneReadOnly(); - assertEqual((typeof(result)).toString(), "typedesc [[{\"id\":1,\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":25}]]"); - assertEqual(result, [table key(id) [{"id":1,"firstName":"John","lastName":"Doe","age":25}]]); + (table key(id))[] & readonly result = from var user in uList + select user.cloneReadOnly(); + assertEqual((typeof (result)).toString(), "typedesc [[{\"id\":1,\"firstName\":\"John\",\"lastName\":\"Doe\",\"age\":25}]]"); + assertEqual(result, [table key(id) [{"id": 1, "firstName": "John", "lastName": "Doe", "age": 25}]]); } function testConstructingListOfRecordsUsingQueryWithReadonly() { - Employee emp1 = {firstName: "A1", lastName: "B1", dept: "C1"}; - Employee emp2 = {firstName: "A2", lastName: "B2", dept: "C2"}; - Employee emp3 = {firstName: "A3", lastName: "B3", dept: "C3"}; + EmployeeX emp1 = {firstName: "A1", lastName: "B1", dept: "C1"}; + EmployeeX emp2 = {firstName: "A2", lastName: "B2", dept: "C2"}; + EmployeeX emp3 = {firstName: "A3", lastName: "B3", dept: "C3"}; - (Employee & readonly)[] & readonly result = from var user in [emp1, emp2, emp3] - select user.cloneReadOnly(); - assertEqual((typeof(result)).toString(), "typedesc [{\"firstName\":\"A1\",\"lastName\":\"B1\",\"dept\":\"C1\"},{\"firstName\":\"A2\",\"lastName\":\"B2\",\"dept\":\"C2\"},{\"firstName\":\"A3\",\"lastName\":\"B3\",\"dept\":\"C3\"}]"); - assertEqual(result, [{"firstName":"A1","lastName":"B1","dept":"C1"},{"firstName":"A2","lastName":"B2","dept":"C2"},{"firstName":"A3","lastName":"B3","dept":"C3"}]); + (EmployeeX & readonly)[] & readonly result = from var user in [emp1, emp2, emp3] + select user.cloneReadOnly(); + assertEqual((typeof (result)).toString(), "typedesc [{\"firstName\":\"A1\",\"lastName\":\"B1\",\"dept\":\"C1\"},{\"firstName\":\"A2\",\"lastName\":\"B2\",\"dept\":\"C2\"},{\"firstName\":\"A3\",\"lastName\":\"B3\",\"dept\":\"C3\"}]"); + assertEqual(result, [{"firstName": "A1", "lastName": "B1", "dept": "C1"}, {"firstName": "A2", "lastName": "B2", "dept": "C2"}, {"firstName": "A3", "lastName": "B3", "dept": "C3"}]); } function testConstructingListOfXMLsUsingQueryWithReadonly() { xml a = xml ` 1 John `; (xml & readonly)[] & readonly result = from var user in a - select user.cloneReadOnly(); - assertEqual((typeof(result)).toString(), "typedesc [` 1 `,` `,` John `]"); - assertEqual(result, [xml` 1 `,xml` `,xml` John `]); + select user.cloneReadOnly(); + assertEqual((typeof (result)).toString(), "typedesc [` 1 `,` `,` John `]"); + assertEqual(result, [xml ` 1 `, xml ` `, xml ` John `]); } type Type1 int[]|string; function testConstructingListOfListsUsingQueryWithReadonly() { Type1[] & readonly result = from var user in [[1, 2], "a", "b", [-1, int:MAX_VALUE]] - select user.cloneReadOnly(); - assertEqual((typeof(result)).toString(), "typedesc [[1,2],\"a\",\"b\",[-1,9223372036854775807]]"); - assertEqual(result, [[1,2],"a","b",[-1,9223372036854775807]]); + select user.cloneReadOnly(); + assertEqual((typeof (result)).toString(), "typedesc [[1,2],\"a\",\"b\",[-1,9223372036854775807]]"); + assertEqual(result, [[1, 2], "a", "b", [-1, 9223372036854775807]]); } function testConstructingListOfMapsUsingQueryWithReadonly() { map[] & readonly result = from var item in [[1, 2], "a", "b", [-1, int:MAX_VALUE]] - select {item: item}.cloneReadOnly(); + select {item: item}.cloneReadOnly(); - assertEqual((typeof(result)).toString(), "typedesc [{\"item\":[1,2]},{\"item\":\"a\"},{\"item\":\"b\"},{\"item\":[-1,9223372036854775807]}]"); - assertEqual(result, [{"item":[1,2]},{"item":"a"},{"item":"b"},{"item":[-1,9223372036854775807]}]); + assertEqual((typeof (result)).toString(), "typedesc [{\"item\":[1,2]},{\"item\":\"a\"},{\"item\":\"b\"},{\"item\":[-1,9223372036854775807]}]"); + assertEqual(result, [{"item": [1, 2]}, {"item": "a"}, {"item": "b"}, {"item": [-1, 9223372036854775807]}]); } type T record { @@ -1497,8 +1562,9 @@ type T record { }; function testConstructingListInRecordsUsingQueryWithReadonly() { - T rec1 = { params: from var s in ["a", "b", "c", "abc"] select s }; - assertEqual(rec1, {"params":["a","b","c","abc"]}); + T rec1 = {params: from var s in ["a", "b", "c", "abc"] + select s}; + assertEqual(rec1, {"params": ["a", "b", "c", "abc"]}); } type DepartmentDetails record { @@ -1513,69 +1579,77 @@ type ErrorOrImmutableMapOfInt ImmutableMapOfInt|error; function testReadonlyMap1() { map & readonly mp1 = map from var item in [["1", 1], ["2", 2], ["3", 3], ["4", 4]] - select item; - any _ = mp1; - assertEqual((typeof(mp1)).toString(), "typedesc {\"1\":1,\"2\":2,\"3\":3,\"4\":4}"); - assertEqual(mp1, {"1":1,"2":2,"3":3,"4":4}); + select item; + any _ = mp1; + assertEqual((typeof (mp1)).toString(), "typedesc {\"1\":1,\"2\":2,\"3\":3,\"4\":4}"); + assertEqual(mp1, {"1": 1, "2": 2, "3": 3, "4": 4}); ImmutableMapOfInt mp2 = map from var item in [["1", 1], ["2", 2], ["3", 3], ["4", 4]] - select item; - any _ = mp2; - assertEqual((typeof(mp2)).toString(), "typedesc {\"1\":1,\"2\":2,\"3\":3,\"4\":4}"); - assertEqual(mp2, {"1":1,"2":2,"3":3,"4":4}); - + select item; + any _ = mp2; + assertEqual((typeof (mp2)).toString(), "typedesc {\"1\":1,\"2\":2,\"3\":3,\"4\":4}"); + assertEqual(mp2, {"1": 1, "2": 2, "3": 3, "4": 4}); ImmutableMapOfDept mp3 = map from var item in ["ABC", "DEF", "XY"] - let DepartmentDetails & readonly dept = {dept: item} - select [item, dept]; - any _ = mp3; - assertEqual((typeof(mp3)).toString(), "typedesc {\"ABC\":{\"dept\":\"ABC\"},\"DEF\":{\"dept\":\"DEF\"},\"XY\":{\"dept\":\"XY\"}}"); - assertEqual(mp3, {"ABC":{"dept":"ABC"},"DEF":{"dept":"DEF"},"XY":{"dept":"XY"}}); + let DepartmentDetails & readonly dept = {dept: item} + select [item, dept]; + any _ = mp3; + assertEqual((typeof (mp3)).toString(), "typedesc {\"ABC\":{\"dept\":\"ABC\"},\"DEF\":{\"dept\":\"DEF\"},\"XY\":{\"dept\":\"XY\"}}"); + assertEqual(mp3, {"ABC": {"dept": "ABC"}, "DEF": {"dept": "DEF"}, "XY": {"dept": "XY"}}); ErrorOrImmutableMapOfInt mp4 = map from var item in [["1", 1], ["2", 2], ["3", 3], ["4", 4]] - where item[1] > 1 - select item; - any _ = (checkpanic mp4); - assertEqual((typeof(mp4)).toString(), "typedesc {\"2\":2,\"3\":3,\"4\":4}"); - assertEqual(mp4, {"2":2,"3":3,"4":4}); + where item[1] > 1 + select item; + any _ = (checkpanic mp4); + assertEqual((typeof (mp4)).toString(), "typedesc {\"2\":2,\"3\":3,\"4\":4}"); + assertEqual(mp4, {"2": 2, "3": 3, "4": 4}); [string:Char, int[]][] & readonly list = [["a", [1, 2]], ["b", [3, 4]], ["c", [4]], ["c", [3]]]; - map|error mp5 = map from var item in list select item; - assertEqual(mp5, {"a":[1,2],"b":[3,4],"c":[3]}); - - map & readonly|error mp6 = map from var item in list select item; - assertEqual(mp6, {"a":[1,2],"b":[3,4],"c":[3]}); - any _ = (checkpanic mp6); - - map & readonly|error mp7 = map from var item in list select item on conflict error("Error"); + map|error mp5 = map from var item in list + select item; + assertEqual(mp5, {"a": [1, 2], "b": [3, 4], "c": [3]}); + + map & readonly|error mp6 = map from var item in list + select item; + assertEqual(mp6, {"a": [1, 2], "b": [3, 4], "c": [3]}); + any _ = (checkpanic mp6); + + map & readonly|error mp7 = map from var item in list + select item + on conflict error("Error"); assertEqual(mp7, error("Error")); } function testReadonlyMap2() { map & readonly mp1 = map from var item in [["1", 1], ["2", 2], ["2", 3], ["4", 4]] - select [item[0], item[1] * 2] on conflict (); - assertEqual(mp1, {"1":2,"2":6,"4":8}); + select [item[0], item[1] * 2] + on conflict (); + assertEqual(mp1, {"1": 2, "2": 6, "4": 8}); ImmutableMapOfInt|error mp2 = map from var item in [["1", 1], ["2", 2], ["2", 3], ["4", 4]] - select item on conflict error("Error 1"); + select item + on conflict error("Error 1"); assertEqual(mp2, error("Error 1")); error? conflictMsg = error("Error 2"); ImmutableMapOfDept|error mp3 = map from var item in ["ABC", "DEF", "XY", "ABC"] - let DepartmentDetails & readonly dept = {dept: item} - select [item, dept] on conflict conflictMsg; + let DepartmentDetails & readonly dept = {dept: item} + select [item, dept] + on conflict conflictMsg; assertEqual(mp3, error("Error 2")); conflictMsg = null; ErrorOrImmutableMapOfInt mp4 = map from var item in [["1", 1], ["2", 2], ["3", 3], ["1", 4]] - where item[1] > 1 - select item on conflict conflictMsg; - assertEqual(mp4, {"2":2,"3":3,"1":4}); + where item[1] > 1 + select item + on conflict conflictMsg; + assertEqual(mp4, {"2": 2, "3": 3, "1": 4}); } class EvenNumberGenerator { int i = 0; - public isolated function next() returns record {| int value; |}|error { + + public isolated function next() returns record {|int value;|}|error { return error("Greater than 20!"); } } @@ -1590,19 +1664,19 @@ type NumberRecord record {| |}; function testQueryConstructingMapsAndTablesWithClausesMayCompleteSEarlyWithError() { - EvenNumberGenerator evenGen = new(); - stream evenNumberStream = new(evenGen); + EvenNumberGenerator evenGen = new (); + stream evenNumberStream = new (evenGen); map|error map1 = map from var item in evenNumberStream - select [item.toBalString(), item]; + select [item.toBalString(), item]; assertEqual(map1, error("Greater than 20!")); table|error table1 = table key() from var item in evenNumberStream - select {value: item}; + select {value: item}; assertEqual(table1, error("Greater than 20!")); table key(id)|error table2 = table key(id) from var item in evenNumberStream - select {id: item, value: item.toBalString()}; + select {id: item, value: item.toBalString()}; assertEqual(table2, error("Greater than 20!")); // Enable following tests after fixing issue - lang/#36746 @@ -1625,16 +1699,18 @@ function testQueryConstructingMapsAndTablesWithClausesMayCompleteSEarlyWithError // assertEqual(table4, error("Greater than 20!")); map|error map3 = map from var firstNo in [1, 4, 4, 10] - select [firstNo.toBalString(), firstNo] on conflict error("Error"); + select [firstNo.toBalString(), firstNo] + on conflict error("Error"); assertEqual(map3, error("Error")); table key(id)|error table6 = table key(id) from var firstNo in [1, 4, 4, 10] - select {id: firstNo, value: firstNo.toBalString()} on conflict error("Error"); + select {id: firstNo, value: firstNo.toBalString()} + on conflict error("Error"); assertEqual(table6, error("Error")); } function testQueryConstructingMapWithOnConflictsWithVarRef() { - map|error mp1 = map from int i in [1, 2, 3, 1, 2, 3] + map|error mp1 = map from int i in [1, 2, 3, 1, 2, 3] let string value = "A" + i.toString() select [i.toString(), value] on conflict error(string `Duplicate Key: ${i} Value: ${value}`); @@ -1652,98 +1728,124 @@ function testQueryConstructingMapWithOnConflictsWithVarRef() { } function testQueryConstructingMapsAndTablesWithClausesMayCompleteSEarlyWithError2() { - EvenNumberGenerator evenGen = new(); - stream evenNumberStream = new(evenGen); + EvenNumberGenerator evenGen = new (); + stream evenNumberStream = new (evenGen); - map|error map1 = map from var item in (stream from var integer in evenNumberStream select integer) - select [item.toBalString(), item]; + map|error map1 = map from var item in (stream from var integer in evenNumberStream + select integer) + select [item.toBalString(), item]; assertEqual(map1, error("Greater than 20!")); - table|error table1 = table key() from var item in (stream from var integer in evenNumberStream select integer) - select {value: item}; + table|error table1 = table key() from var item in (stream from var integer in evenNumberStream + select integer) + select {value: item}; assertEqual(table1, error("Greater than 20!")); - table key(id)|error table2 = table key(id) from var item in (stream from var integer in evenNumberStream select integer) - select {id: item, value: item.toBalString()}; + table key(id)|error table2 = table key(id) from var item in (stream from var integer in evenNumberStream + select integer) + select {id: item, value: item.toBalString()}; assertEqual(table2, error("Greater than 20!")); - map|error map3 = map from var item in (stream from var integer in (stream from var integer in evenNumberStream select integer) select integer) - select [item.toBalString(), item]; + map|error map3 = map from var item in (stream from var integer in (stream from var integer in evenNumberStream + select integer) + select integer) + select [item.toBalString(), item]; assertEqual(map3, error("Greater than 20!")); table|error table4 = table key() from var item in - (stream from var integer in (stream from var integer in evenNumberStream select integer) select integer) - select {value: item}; + (stream from var integer in (stream from var integer in evenNumberStream + select integer) + select integer) + select {value: item}; assertEqual(table4, error("Greater than 20!")); } type FooBar1 ("foo"|"bar"|string)[2]; + type FooBar2 ("foo"|"bar")[2]; + type FooBar3 "foo"|"bar"; + type FooBar4 "foo"|"bar"|string:Char; + type FooBar5 "foo"|"bar"|string; function testMapConstructingQueryExprWithStringSubtypes() { FooBar1[] list1 = [["key1", "foo"], ["key2", "foo"], ["key3", "foo"]]; - map|error mp1 = map from var item in list1 select item; - assertEqual(mp1, {"key1":"foo","key2":"foo","key3":"foo"}); + map|error mp1 = map from var item in list1 + select item; + assertEqual(mp1, {"key1": "foo", "key2": "foo", "key3": "foo"}); FooBar2[] list2 = [["foo", "foo"], ["bar", "foo"], ["foo", "foo"]]; - map|error mp2 = map from var item in list2 select item; - assertEqual(mp2, {"foo":"foo","bar":"foo"}); + map|error mp2 = map from var item in list2 + select item; + assertEqual(mp2, {"foo": "foo", "bar": "foo"}); FooBar3[][2] list3 = [["foo", "bar"], ["bar", "foo"], ["foo", "bar"]]; - map|error mp3 = map from var item in list3 select item; - assertEqual(mp3, {"foo":"bar","bar":"foo"}); + map|error mp3 = map from var item in list3 + select item; + assertEqual(mp3, {"foo": "bar", "bar": "foo"}); FooBar4[][2] list4 = [["foo", "4"], ["bar", "2"], ["foo", "3"]]; - map|error mp4 = map from var item in list4 select item; - assertEqual(mp4, {"foo":"3","bar":"2"}); - map|error mp5 = map from var item in list4 select item on conflict error("Error"); + map|error mp4 = map from var item in list4 + select item; + assertEqual(mp4, {"foo": "3", "bar": "2"}); + map|error mp5 = map from var item in list4 + select item + on conflict error("Error"); assertEqual(mp5, error("Error")); FooBar5[][2] list5 = [["key1", "1.4"], ["key2", "2"], ["key3", "3"]]; - map|error mp6 = map from var item in list5 select item; - assertEqual(mp6, {"key1":"1.4","key2":"2","key3":"3"}); + map|error mp6 = map from var item in list5 + select item; + assertEqual(mp6, {"key1": "1.4", "key2": "2", "key3": "3"}); [FooBar3, int|float][] list6 = [["foo", 1.4], ["bar", 2], ["foo", 3]]; - map|error mp7 = map from var item in list6 select item; - assertEqual(mp7, {"foo":3,"bar":2}); - map|error mp8 = map from var item in list6 select item on conflict error("Error"); + map|error mp7 = map from var item in list6 + select item; + assertEqual(mp7, {"foo": 3, "bar": 2}); + map|error mp8 = map from var item in list6 + select item + on conflict error("Error"); assertEqual(mp8, error("Error")); [FooBar4, int|float][] list7 = [["foo", 1.4], ["bar", 2], ["foo", 3]]; - map|error mp9 = map from var item in list7 select item; - assertEqual(mp9, {"foo":3,"bar":2}); - map|error mp10 = map from var item in list7 select item on conflict error("Error"); + map|error mp9 = map from var item in list7 + select item; + assertEqual(mp9, {"foo": 3, "bar": 2}); + map|error mp10 = map from var item in list7 + select item + on conflict error("Error"); assertEqual(mp10, error("Error")); [FooBar5, int|float][] list8 = [["key1", 1.4], ["key2", 2], ["key3", 3]]; - map|error mp11 = map from var item in list8 select item; - assertEqual(mp11, {"key1":1.4,"key2":2,"key3":3}); + map|error mp11 = map from var item in list8 + select item; + assertEqual(mp11, {"key1": 1.4, "key2": 2, "key3": 3}); } function testDiffQueryConstructsUsedAsFuncArgs() returns error? { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; + CustomerX[] customerList = [c1, c2, c3]; int tblLength = getTableLength(table key(id, name) from var customer in customerList - select { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - }); + select { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + }); assertEqual(tblLength, 3); FooBar1[] list1 = [["key1", "foo"], ["key2", "foo"], ["key3", "foo"]]; - int mapLength = getMapLength(map from var item in list1 select item); + int mapLength = getMapLength(map from var item in list1 + select item); assertEqual(mapLength, 3); } -function getTableLength(CustomerTable tbl) returns int { +function getTableLength(CustomerTableX tbl) returns int { return tbl.length(); } @@ -1837,9 +1939,9 @@ function testJoinedQueryExprConstructingMapWithRegExp() { let string:RegExp a = re `AB*(A|B|[ab-fgh]+(?im-x:[cdeg-k]??${v})|)|^|PQ?` select [re1.toString() + "1", re1.toString() + a.toString()]; assertEqual({ - A1: "AAB*(A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?", - B1: "BAB*(A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?" - }, arr3); + A1: "AAB*(A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?", + B1: "BAB*(A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?" + }, arr3); } type ModuleDecls [string, FuncDecl...]; @@ -1866,67 +1968,69 @@ function testInnerQueryConstructedWithCEP() { select [name, sig] ]; - assertEqual([["01",["func1",["foo",["int","string"],"boolean"]]],["02"]], decl); + assertEqual([["01", ["func1", ["foo", ["int", "string"], "boolean"]]], ["02"]], decl); } error onConflictError = error("Key Conflict", message = "cannot insert."); function testTableConstructQueryWithNonConflictingKeys() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3]; - CustomerTable|error customerTable = getQueryResult(onConflictError, customerList); - assertEqual(true, customerTable is CustomerTable); - CustomerTable expectedTableValue = table [{id: 1, name: "Melina", noOfItems: 12}, - {id: 2, name: "James", noOfItems: 5}, - {id: 3, name: "Anne", noOfItems: 20}]; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX[] customerList = [c1, c2, c3]; + CustomerTableX|error customerTable = getQueryResult(onConflictError, customerList); + assertEqual(true, customerTable is CustomerTableX); + CustomerTableX expectedTableValue = table [ + {id: 1, name: "Melina", noOfItems: 12}, + {id: 2, name: "James", noOfItems: 5}, + {id: 3, name: "Anne", noOfItems: 20} + ]; assertEqual(customerTable, expectedTableValue); } function testTableConstructQueryWithConflictingKeys() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3, c1]; - CustomerTable|error customerTable = getQueryResult(onConflictError, customerList); + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX[] customerList = [c1, c2, c3, c1]; + CustomerTableX|error customerTable = getQueryResult(onConflictError, customerList); assertEqual(customerTable is error, true); assertEqual((customerTable).message(), "Key Conflict"); } -function getQueryResult(error onConflictError, Customer[] customerList) returns CustomerTable|error { - return table key(id, name) from var customer in customerList - select { +function getQueryResult(error onConflictError, CustomerX[] customerList) returns CustomerTableX|error { + return table key(id, name) from var customer in customerList + select { id: customer.id, name: customer.name, noOfItems: customer.noOfItems - } - on conflict onConflictError; + } + on conflict onConflictError; } function testMapConstructNestedQueryWithConflictingKeys() { - Customer c1 = {id: 1, name: "Melina", noOfItems: 12}; - Customer c2 = {id: 2, name: "James", noOfItems: 5}; - Customer c3 = {id: 3, name: "Anne", noOfItems: 20}; - Customer[] customerList = [c1, c2, c3, c1]; + CustomerX c1 = {id: 1, name: "Melina", noOfItems: 12}; + CustomerX c2 = {id: 2, name: "James", noOfItems: 5}; + CustomerX c3 = {id: 3, name: "Anne", noOfItems: 20}; + CustomerX[] customerList = [c1, c2, c3, c1]; (anydata|error)[] result = from var i in [1] select table key(id, name) from var customer in customerList - select { - id: customer.id, - name: customer.name, - noOfItems: customer.noOfItems - } - on conflict error(string `Error key: ${customer.id} iteration: ${i}`); + select { + id: customer.id, + name: customer.name, + noOfItems: customer.noOfItems + } + on conflict error(string `Error key: ${customer.id} iteration: ${i}`); assertEqual(result[0] is error, true); assertEqual((result[0]).message(), "Error key: 1 iteration: 1"); } function testMapConstructQueryWithConflictingKeys() { - Customer c1 = {id: 1, name: "Abba", noOfItems: 10}; - Customer c2 = {id: 2, name: "Jim", noOfItems: 20}; - Customer c3 = {id: 3, name: "James", noOfItems: 30}; - Customer c4 = {id: 3, name: "Abba", noOfItems: 40}; - Customer[] customerList = [c1, c2, c3, c4]; + CustomerX c1 = {id: 1, name: "Abba", noOfItems: 10}; + CustomerX c2 = {id: 2, name: "Jim", noOfItems: 20}; + CustomerX c3 = {id: 3, name: "James", noOfItems: 30}; + CustomerX c4 = {id: 3, name: "Abba", noOfItems: 40}; + CustomerX[] customerList = [c1, c2, c3, c4]; anydata|error result = map from var {name, noOfItems} in customerList group by name select [name, [noOfItems]] diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-defined-type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-defined-type.bal index 10b35e903ead..344c163bc83b 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-defined-type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-defined-type.bal @@ -14,7 +14,7 @@ // specific language governing permissions and limitations // under the License. -type Person record {| +type PersonK record {| string firstName; string lastName; int age; @@ -33,35 +33,35 @@ type Employee record {| |}; type Person1 record {| - string firstName; - string lastName; - string deptAccess; - Address address; + string firstName; + string lastName; + string deptAccess; + Address address; |}; -type Address record{| +type Address record {| string city; string country; |}; -type Student record{| +type Student record {| string firstName; string lastName; float score; |}; -type Teacher1 record{ - //record type referencing - *Person1; - Student[] classStudents?; - //anonymous record type - record {| - int duration; - string qualitifications; - |} experience; +type Teacher1 record { + //record type referencing + *Person1; + Student[] classStudents?; + //anonymous record type + record {| + int duration; + string qualitifications; + |} experience; }; -type Subscription record{| +type Subscription record {| string firstName; string lastName; float score; @@ -70,6 +70,7 @@ type Subscription record{| class NumberGenerator { int i = 0; + public isolated function next() returns record {|int value;|}|error? { //closes the stream after 5 events if (self.i == 5) { @@ -82,6 +83,7 @@ class NumberGenerator { class NumberGeneratorWithError { int i = 0; + public isolated function next() returns record {|int value;|}|error? { if (self.i == 2) { return error("Custom error thrown explicitly."); @@ -93,6 +95,7 @@ class NumberGeneratorWithError { class NumberGeneratorWithError2 { int i = 0; + public isolated function next() returns record {|int value;|}|error { if (self.i == 2) { return error("Custom error thrown explicitly."); @@ -106,6 +109,7 @@ type ErrorR1 error>; class NumberGeneratorWithCustomError { int i = 0; + public isolated function next() returns record {|int value;|}|ErrorR1? { if (self.i == 3) { return error ErrorR1("Custom error", x = 1); @@ -116,10 +120,10 @@ class NumberGeneratorWithCustomError { } type ResultValue record {| - Person value; + PersonK value; |}; -function getRecordValue((record {| Person value; |}|error?)|(record {| Person value; |}?) returnedVal) returns Person? { +function getRecordValue((record {|PersonK value;|}|error?)|(record {|PersonK value;|}?) returnedVal) returns PersonK? { if (returnedVal is ResultValue) { return returnedVal.value; } else { @@ -127,97 +131,97 @@ function getRecordValue((record {| Person value; |}|error?)|(record {| Person va } } -function testSimpleSelectQueryWithSimpleVariable() returns Person[] { +function testSimpleSelectQueryWithSimpleVariable() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; - Person[] outputPersonList = + PersonK[] outputPersonList = from var person in personList - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } -function testSimpleSelectQueryWithRecordVariable() returns Person[] { +function testSimpleSelectQueryWithRecordVariable() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; - Person[] outputPersonList = - from var { firstName: nm1, lastName: nm2, age: a } in personList - select { - firstName: nm1, - lastName: nm2, - age: a - }; + PersonK[] outputPersonList = + from var {firstName: nm1, lastName: nm2, age: a} in personList + select { + firstName: nm1, + lastName: nm2, + age: a + }; return outputPersonList; } -function testSimpleSelectQueryWithRecordVariableV2() returns Person[] { +function testSimpleSelectQueryWithRecordVariableV2() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; - Person[] outputPersonList = - from var { firstName, lastName, age } in personList - select { - firstName: firstName, - lastName: lastName, - age: age - }; + PersonK[] outputPersonList = + from var {firstName, lastName, age} in personList + select { + firstName: firstName, + lastName: lastName, + age: age + }; return outputPersonList; } function testSimpleSelectQueryWithRecordVariableV3() returns Teacher[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; Teacher[] outputPersonList = - from var { firstName, lastName, age } in personList - select { - firstName, - lastName - }; + from var {firstName, lastName, age} in personList + select { + firstName, + lastName + }; return outputPersonList; } -function testSimpleSelectQueryWithWhereClause() returns Person[] { +function testSimpleSelectQueryWithWhereClause() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; - Person[] outputPersonList = + PersonK[] outputPersonList = from var person in personList - where person.age >= 30 - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + where person.age >= 30 + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } @@ -227,8 +231,8 @@ function testQueryExpressionForPrimitiveType() returns boolean { int[] outputIntList = from var value in intList - where value > 20 - select value; + where value > 20 + select value; return outputIntList == [21, 25]; } @@ -239,123 +243,123 @@ function testQueryExpressionWithSelectExpression() returns boolean { string[] stringOutput = from var value in intList - select value.toString(); + select value.toString(); - return stringOutput == ["1","2", "3"]; + return stringOutput == ["1", "2", "3"]; } -function testFilteringNullElements() returns Person[] { +function testFilteringNullElements() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person?[] personList = [p1, (), p2]; + PersonK?[] personList = [p1, (), p2]; - Person[] outputPersonList = - from var person in personList - where (person is Person) - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + PersonK[] outputPersonList = + from var person in personList + where (person is PersonK) + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } function testMapWithArity() returns boolean { map m = {a: "1A", b: "2B", c: "3C", d: "4D"}; string[] val = from var v in m - where v == "1A" - select v; + where v == "1A" + select v; return val == ["1A"]; } function testJSONArrayWithArity() returns boolean { json[] jdata = [{name: "bob", age: 10}, {name: "tom", age: 16}]; string[] val = from var v in jdata - select checkpanic v.name; + select checkpanic v.name; return val == ["bob", "tom"]; } function testArrayWithTuple() returns boolean { [int, string][] arr = [[1, "A"], [2, "B"], [3, "C"]]; string[] val = from var [i, v] in arr - where i == 3 - select v; + where i == 3 + select v; return val == ["C"]; } -function testFromClauseWithStream() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 30}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 40}; - Person p3 = {firstName: "John", lastName: "David", age: 50}; +function testFromClauseWithStream() returns PersonK[] { + PersonK p1 = {firstName: "Alex", lastName: "George", age: 30}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 40}; + PersonK p3 = {firstName: "John", lastName: "David", age: 50}; - Person[] personList = [p1, p2, p3]; - stream streamedPersons = personList.toStream(); + PersonK[] personList = [p1, p2, p3]; + stream streamedPersons = personList.toStream(); - Person[] outputPersonList = + PersonK[] outputPersonList = from var person in streamedPersons - where person.age == 40 - select person; + where person.age == 40 + select person; return outputPersonList; } function testSimpleSelectQueryWithLetClause() returns Employee[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; Employee[] outputPersonList = from var person in personList - let string depName = "HR", string companyName = "WSO2" - where person.age >= 30 - select { - firstName: person.firstName, - lastName: person.lastName, - department: depName, - company: companyName - }; + let string depName = "HR", string companyName = "WSO2" + where person.age >= 30 + select { + firstName: person.firstName, + lastName: person.lastName, + department: depName, + company: companyName + }; return outputPersonList; } -function testFunctionCallInVarDeclLetClause() returns Person[] { +function testFunctionCallInVarDeclLetClause() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person[] personList = [p1, p2]; + PersonK[] personList = [p1, p2]; var outputPersonList = - from Person person in personList - let int twiceAge = mutiplyBy2(person.age) - select { - firstName: person.firstName, - lastName: person.lastName, - age: twiceAge - }; + from PersonK person in personList + let int twiceAge = mutiplyBy2(person.age) + select { + firstName: person.firstName, + lastName: person.lastName, + age: twiceAge + }; return outputPersonList; } -function testUseOfLetInWhereClause() returns Person[] { +function testUseOfLetInWhereClause() returns PersonK[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 18}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 22}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 18}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 22}; - Person[] personList = [p1, p2]; + PersonK[] personList = [p1, p2]; var outputPersonList = from var person in personList - let int twiceAge = mutiplyBy2(person.age) - where twiceAge > 40 - select { - firstName: person.firstName, - lastName: person.lastName, - age: twiceAge - }; + let int twiceAge = mutiplyBy2(person.age) + where twiceAge > 40 + select { + firstName: person.firstName, + lastName: person.lastName, + age: twiceAge + }; return outputPersonList; } @@ -369,8 +373,8 @@ public function testQueryWithStream() returns boolean { var numberStream = new stream(numGen); int[]|error oddNumberList = from int num in numberStream - where (num % 2 == 1) - select num; + where (num % 2 == 1) + select num; if (oddNumberList is error) { return false; } else { @@ -378,14 +382,13 @@ public function testQueryWithStream() returns boolean { } } - public function testQueryStreamWithError() { NumberGeneratorWithError numGen = new; var numberStream = new stream(numGen); int[]|error oddNumberList = from int num in numberStream - where (num % 2 == 1) - select num; + where (num % 2 == 1) + select num; if (oddNumberList is error) { return; } @@ -452,10 +455,10 @@ public function testQueryStreamWithDifferentCompletionTypes() { } } -function testOthersAssociatedWithRecordTypes() returns Teacher1[]{ +function testOthersAssociatedWithRecordTypes() returns Teacher1[] { - Person1 p1 = {firstName: "Alex", lastName: "George", deptAccess: "XYZ", address:{city:"NY", country:"America"}}; - Person1 p2 = {firstName: "Ranjan", lastName: "Fonseka", deptAccess: "XYZ", address:{city:"NY", country:"America"}}; + Person1 p1 = {firstName: "Alex", lastName: "George", deptAccess: "XYZ", address: {city: "NY", country: "America"}}; + Person1 p2 = {firstName: "Ranjan", lastName: "Fonseka", deptAccess: "XYZ", address: {city: "NY", country: "America"}}; Student s1 = {firstName: "Alex", lastName: "George", score: 82.5}; Student s2 = {firstName: "Ranjan", lastName: "Fonseka", score: 90.6}; @@ -464,142 +467,142 @@ function testOthersAssociatedWithRecordTypes() returns Teacher1[]{ Student[] studentList = [s1, s2]; Teacher1[] outputteacherList = - from var person in personList - let int period = 10, string degree = "B.Sc." - select{ - //change order of the record fields - firstName:person.firstName, - address: person.address, - //optional field - classStudents: studentList, - deptAccess: person.deptAccess, - //member access - lastName:person["lastName"], - //values for anonymous record fields - experience: { - duration: period, - qualitifications: degree - } - }; - - return outputteacherList; + from var person in personList + let int period = 10, string degree = "B.Sc." + select { + //change order of the record fields + firstName: person.firstName, + address: person.address, + //optional field + classStudents: studentList, + deptAccess: person.deptAccess, + //member access + lastName: person["lastName"], + //values for anonymous record fields + experience: { + duration: period, + qualitifications: degree + } + }; + + return outputteacherList; } function testQueryExprTupleTypedBinding2() returns boolean { - [int,int][] arr1 = [[1,2],[2,3],[3,4]]; - [int,int] arr2 = [1,2]; + [int, int][] arr1 = [[1, 2], [2, 3], [3, 4]]; + [int, int] arr2 = [1, 2]; int[] ouputList = - from [int,int] [a,b] in arr1 - let [int,int] [d1,d2] = arr2, int x=d1+d2 - where b > x - select a; + from [int, int] [a, b] in arr1 + let [int, int] [d1, d2] = arr2, int x = d1 + d2 + where b > x + select a; - return ouputList == [3]; + return ouputList == [3]; } -function testQueryExprWithTypeConversion() returns Person1[]{ +function testQueryExprWithTypeConversion() returns Person1[] { - Person1 p1 = {firstName: "Alex", lastName: "George", deptAccess: "XYZ", address:{city:"NY", country:"America"}}; - Person1 p2 = {firstName: "Ranjan", lastName: "Fonseka", deptAccess: "XYZ", address:{city:"NY", country:"America"}}; + Person1 p1 = {firstName: "Alex", lastName: "George", deptAccess: "XYZ", address: {city: "NY", country: "America"}}; + Person1 p2 = {firstName: "Ranjan", lastName: "Fonseka", deptAccess: "XYZ", address: {city: "NY", country: "America"}}; - map m = {city:"New York", country:"America"}; + map m = {city: "New York", country: "America"}; - Person1[] personList = [p1, p2]; + Person1[] personList = [p1, p2]; - Person1[] outputPersonList = - from var person in personList - select{ - firstName: person.firstName, - lastName: person.lastName, - deptAccess: person.deptAccess, - address: checkpanic m.cloneWithType(Address) - }; + Person1[] outputPersonList = + from var person in personList + select { + firstName: person.firstName, + lastName: person.lastName, + deptAccess: person.deptAccess, + address: checkpanic m.cloneWithType(Address) + }; - return outputPersonList; + return outputPersonList; } -function testQueryExprWithStreamMapAndFilter() returns Subscription[]{ +function testQueryExprWithStreamMapAndFilter() returns Subscription[] { Student s1 = {firstName: "Alex", lastName: "George", score: 82.5}; Student s2 = {firstName: "Ranjan", lastName: "Fonseka", score: 90.6}; Student[] studentList = [s1, s2]; - Subscription[] outputSubscriptionList = - from var subs in >studentList.toStream().filter(function (Student student) returns boolean { - return student.score > 85.3; - }).'map(function (Student student) returns Subscription { - Subscription subscription = { - firstName: student.firstName, - lastName: student.lastName, - score: student.score, - degree: "Bachelor of Medicine" - }; - return subscription; - }) - select subs; + Subscription[] outputSubscriptionList = + from var subs in >studentList.toStream().filter(function(Student student) returns boolean { + return student.score > 85.3; + }).'map(function(Student student) returns Subscription { + Subscription subscription = { + firstName: student.firstName, + lastName: student.lastName, + score: student.score, + degree: "Bachelor of Medicine" + }; + return subscription; + }) + select subs; - return outputSubscriptionList; + return outputSubscriptionList; } function testSimpleSelectQueryReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; - stream outputPersonStream = stream from var person in personList - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; - Person? returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p1); + stream outputPersonStream = stream from var person in personList + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; + PersonK? returnedVal = getRecordValue(outputPersonStream.next()); + testPassed = testPassed && (returnedVal is PersonK) && (returnedVal == p1); returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p2); + testPassed = testPassed && (returnedVal is PersonK) && (returnedVal == p2); returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p3); + testPassed = testPassed && (returnedVal is PersonK) && (returnedVal == p3); return testPassed; } function testQueryWithRecordVarInLetClause() returns Person1[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonK p3 = {firstName: "John", lastName: "David", age: 33}; Address address = {city: "Colombo", country: "SL"}; - Person[] personList = [p1, p2, p3]; + PersonK[] personList = [p1, p2, p3]; var outputPersonList = from var person in personList - let Address {city: town, country: state } = address - where person.age >= 30 - select { - firstName: person.firstName, - lastName: person.lastName, - deptAccess: "XYZ", - address: {city: town, country: state} - }; + let Address {city: town, country: state} = address + where person.age >= 30 + select { + firstName: person.firstName, + lastName: person.lastName, + deptAccess: "XYZ", + address: {city: town, country: state} + }; return outputPersonList; } function testForeachStream() returns boolean { - Person p1 = {firstName: "Alex", lastName: "George", age: 30}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 40}; - Person p3 = {firstName: "John", lastName: "David", age: 50}; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 30}; + PersonK p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 40}; + PersonK p3 = {firstName: "John", lastName: "David", age: 50}; - Person[] personList = [p1, p2, p3]; - stream streamedPersons = personList.toStream(); + PersonK[] personList = [p1, p2, p3]; + stream streamedPersons = personList.toStream(); - Person[] outputPersonList = []; - foreach Person person in streamedPersons { + PersonK[] outputPersonList = []; + foreach PersonK person in streamedPersons { if (person.age == 40) { outputPersonList.push(person); } @@ -614,8 +617,8 @@ function testForeachStream() returns boolean { function testTypeTestInWhereClause() { int?[] v = [1, 2, (), 3]; int[] result = from var i in v - where i is int - select i; + where i is int + select i; assertEquality(3, result.length()); assertEquality(1, result[0]); assertEquality(2, result[1]); @@ -718,16 +721,16 @@ function testJoinedQueryExprWithRegExp() { let string:RegExp a = re `AB*[^abc-efg](?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??${v})|)|^|PQ?` select re1.toString() + a.toString(); assertEquality(true, [ - "AAB*[^abc-efg](?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?", - "BAB*[^abc-efg](?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?" - ] == arr3); + "AAB*[^abc-efg](?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?", + "BAB*[^abc-efg](?:A|B|[ab-fgh]+(?im-x:[cdeg-k]??1)|)|^|PQ?" + ] == arr3); } function testQueryExprWithLangLibCallsWithArrowFunctions() { - Person p1 = {firstName: "Alex", lastName: "George", age: 30}; - Person p2 = {firstName: "Anne", lastName: "Frank", age: 40}; - Person p3 = {firstName: "John", lastName: "David", age: 50}; - Person[] personList = [p1, p2, p3]; + PersonK p1 = {firstName: "Alex", lastName: "George", age: 30}; + PersonK p2 = {firstName: "Anne", lastName: "Frank", age: 40}; + PersonK p3 = {firstName: "John", lastName: "David", age: 50}; + PersonK[] personList = [p1, p2, p3]; int[] ageList = [50, 60]; int[] filteredAges = from int age in ageList @@ -744,7 +747,7 @@ function testQueryExprWithLangLibCallsWithArrowFunctions() { select ["John", "Frank"].filter(names => names == firstName).pop(); assertEquality(true, filteredNames2 == ["John"]); - Person[][] filteredPersons = from int age in [50] + PersonK[][] filteredPersons = from int age in [50] let string name = personList.filter(person => person.age == age).pop().firstName select personList.filter(person => person.firstName == name); assertEquality(true, filteredPersons == [[{"firstName":"John", "lastName":"David", "age":50}]]); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-var-type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-var-type.bal index 401b7a68af22..48af7090ea13 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-var-type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/query/simple-query-with-var-type.bal @@ -14,13 +14,13 @@ // specific language governing permissions and limitations // under the License. -type Person record {| +type PersonQT record {| string firstName; string lastName; int age; |}; -type Teacher record {| +type TeacherQT record {| string firstName; string lastName; int age; @@ -34,7 +34,7 @@ type EmployeeEntity record { int age; }; -type Employee record {| +type EmployeeQT record {| string fname; string lname; int age; @@ -42,6 +42,7 @@ type Employee record {| class NumberGenerator { int i = 0; + public isolated function next() returns record {|int value;|}|error? { //closes the stream after 5 events if (self.i == 5) { @@ -53,104 +54,104 @@ class NumberGenerator { } type ResultValue record {| - Person value; + PersonQT value; |}; -function getRecordValue((record {| Person value; |}|error?)|(record {| Person value; |}?) returnedVal) returns Person? { - if (returnedVal is ResultValue) { - return returnedVal.value; - } else { - return (); - } +function getRecordValue((record {|PersonQT value;|}|error?)|(record {|PersonQT value;|}?) returnedVal) returns PersonQT? { + if (returnedVal is ResultValue) { + return returnedVal.value; + } else { + return (); + } } -function testSimpleSelectQueryWithSimpleVariable() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; +function testSimpleSelectQueryWithSimpleVariable() returns PersonQT[] { + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; - Person[] outputPersonList = + PersonQT[] outputPersonList = from var person in personList - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } -function testSimpleSelectQueryWithRecordVariable() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; +function testSimpleSelectQueryWithRecordVariable() returns PersonQT[] { + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; - Person[] outputPersonList = - from var { firstName: nm1, lastName: nm2, age: a } in personList - select { - firstName: nm1, - lastName: nm2, - age: a - }; + PersonQT[] outputPersonList = + from var {firstName: nm1, lastName: nm2, age: a} in personList + select { + firstName: nm1, + lastName: nm2, + age: a + }; return outputPersonList; } -function testSimpleSelectQueryWithRecordVariableV2() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; +function testSimpleSelectQueryWithRecordVariableV2() returns PersonQT[] { + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; var outputPersonList = - from var { firstName, lastName, age } in personList - select { - firstName: firstName, - lastName: lastName, - age: age - }; + from var {firstName, lastName, age} in personList + select { + firstName: firstName, + lastName: lastName, + age: age + }; return outputPersonList; } -function testSimpleSelectQueryWithRecordVariableV3() returns Person[] { - Teacher p1 = {firstName: "Alex", lastName: "George", age: 23, teacherId: "XYZ01"}; - Teacher p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30, teacherId: "ABC01"}; - Teacher p3 = {firstName: "John", lastName: "David", age: 33, teacherId: "ABC10"}; +function testSimpleSelectQueryWithRecordVariableV3() returns PersonQT[] { + TeacherQT p1 = {firstName: "Alex", lastName: "George", age: 23, teacherId: "XYZ01"}; + TeacherQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30, teacherId: "ABC01"}; + TeacherQT p3 = {firstName: "John", lastName: "David", age: 33, teacherId: "ABC10"}; - Teacher[] teacherList = [p1, p2, p3]; + TeacherQT[] teacherList = [p1, p2, p3]; var outputPersonList = - from var { firstName, lastName, age, teacherId} in teacherList - select { - firstName: firstName, - lastName: lastName, - age: age - }; + from var {firstName, lastName, age, teacherId} in teacherList + select { + firstName: firstName, + lastName: lastName, + age: age + }; return outputPersonList; } -function testSimpleSelectQueryWithWhereClause() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; +function testSimpleSelectQueryWithWhereClause() returns PersonQT[] { + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; var outputPersonList = from var person in personList - where person.age >= 30 - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + where person.age >= 30 + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } @@ -160,8 +161,8 @@ function testQueryExpressionForPrimitiveType() returns boolean { var outputIntList = from var value in intList - where value > 20 - select value; + where value > 20 + select value; return outputIntList == [21, 25]; } @@ -172,102 +173,102 @@ function testQueryExpressionWithSelectExpression() returns boolean { var stringOutput = from var value in intList - select value.toString(); + select value.toString(); return stringOutput == ["1", "2", "3"]; } -function testFilteringNullElements() returns Person[] { +function testFilteringNullElements() returns PersonQT[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person?[] personList = [p1, (), p2]; + PersonQT?[] personList = [p1, (), p2]; var outputPersonList = - from var person in personList - where (person is Person) - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; + from var person in personList + where (person is PersonQT) + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; return outputPersonList; } function testMapWithArity() returns boolean { map m = {a: "1A", b: "2B", c: "3C", d: "4D"}; var val = map from var v in m - where v == "1A" - select ["a", v]; + where v == "1A" + select ["a", v]; return val == {a: "1A"}; } function testJSONArrayWithArity() returns boolean { json[] jdata = [{name: "bob", age: 10}, {name: "tom", age: 16}]; var val = from var v in jdata - select checkpanic v.name; + select checkpanic v.name; return val == ["bob", "tom"]; } function testArrayWithTuple() returns boolean { [int, string][] arr = [[1, "A"], [2, "B"], [3, "C"]]; var val = from var [i, v] in arr - where i == 3 - select v; + where i == 3 + select v; return val == ["C"]; } -function testQueryExpressionWithVarType() returns Teacher[] { +function testQueryExpressionWithVarType() returns TeacherQT[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; var outputPersonList = from var person in personList - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age, - teacherId: "TER1200" - }; + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age, + teacherId: "TER1200" + }; return outputPersonList; } -function testSimpleSelectQueryWithSpreadOperator() returns Person[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; +function testSimpleSelectQueryWithSpreadOperator() returns PersonQT[] { + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; - Person[] outputPersonList = + PersonQT[] outputPersonList = from var person in personList - select { - ...person - }; + select { + ...person + }; return outputPersonList; } -function testQueryExpressionWithSpreadOperatorV2() returns Teacher[] { +function testQueryExpressionWithSpreadOperatorV2() returns TeacherQT[] { - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; var outputPersonList = from var person in personList - select { - ...person, - teacherId: "TER1200" - }; + select { + ...person, + teacherId: "TER1200" + }; return outputPersonList; } @@ -277,11 +278,11 @@ public function testQueryWithStream() returns boolean { var numberStream = new stream(numGen); var oddNumberList = stream from var num in numberStream - where (num % 2 == 1) - select num; + where (num % 2 == 1) + select num; int[] result = []; - record {| int value; |}|error? v = oddNumberList.next(); - while (v is record {| int value; |}) { + record {|int value;|}|error? v = oddNumberList.next(); + while (v is record {|int value;|}) { result.push(v.value); v = oddNumberList.next(); } @@ -290,26 +291,26 @@ public function testQueryWithStream() returns boolean { function testSimpleSelectQueryReturnStream() returns boolean { boolean testPassed = true; - Person p1 = {firstName: "Alex", lastName: "George", age: 23}; - Person p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; - Person p3 = {firstName: "John", lastName: "David", age: 33}; + PersonQT p1 = {firstName: "Alex", lastName: "George", age: 23}; + PersonQT p2 = {firstName: "Ranjan", lastName: "Fonseka", age: 30}; + PersonQT p3 = {firstName: "John", lastName: "David", age: 33}; - Person[] personList = [p1, p2, p3]; + PersonQT[] personList = [p1, p2, p3]; var outputPersonStream = stream from var person in personList - select { - firstName: person.firstName, - lastName: person.lastName, - age: person.age - }; - Person? returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p1); + select { + firstName: person.firstName, + lastName: person.lastName, + age: person.age + }; + PersonQT? returnedVal = getRecordValue(outputPersonStream.next()); + testPassed = testPassed && (returnedVal is PersonQT) && (returnedVal == p1); returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p2); + testPassed = testPassed && (returnedVal is PersonQT) && (returnedVal == p2); returnedVal = getRecordValue(outputPersonStream.next()); - testPassed = testPassed && (returnedVal is Person) && (returnedVal == p3); + testPassed = testPassed && (returnedVal is PersonQT) && (returnedVal == p3); return testPassed; } @@ -318,14 +319,15 @@ string fname = ""; function testVariableShadowingWithQueryExpressions1() returns boolean { EmployeeEntity[] entities = [ - {id: 1232, fname: "Sameera", lname: "Jayasoma", age: 30}, - {id: 1232, fname: "Asanthi", lname: "Kulasinghe", age: 30}, - {id: 1232, fname: "Khiana", lname: "Jayasoma", age: 2} - ]; + {id: 1232, fname: "Sameera", lname: "Jayasoma", age: 30}, + {id: 1232, fname: "Asanthi", lname: "Kulasinghe", age: 30}, + {id: 1232, fname: "Khiana", lname: "Jayasoma", age: 2} + ]; - Employee[] records = from var {fname, lname, age} in entities select {fname, lname, age}; + EmployeeQT[] records = from var {fname, lname, age} in entities + select {fname, lname, age}; boolean testPassed = true; - Employee e = records[0]; + EmployeeQT e = records[0]; testPassed = testPassed && e.fname == "Sameera" && e.lname == "Jayasoma" && e.age == 30; e = records[1]; testPassed = testPassed && e.fname == "Asanthi" && e.lname == "Kulasinghe" && e.age == 30; @@ -337,15 +339,16 @@ function testVariableShadowingWithQueryExpressions1() returns boolean { function testVariableShadowingWithQueryExpressions2() returns boolean { EmployeeEntity[] entities = [ - {id: 1232, fname: "Sameera", lname: "Jayasoma", age: 30}, - {id: 1232, fname: "Asanthi", lname: "Kulasinghe", age: 30}, - {id: 1232, fname: "Khiana", lname: "Jayasoma", age: 2} - ]; + {id: 1232, fname: "Sameera", lname: "Jayasoma", age: 30}, + {id: 1232, fname: "Asanthi", lname: "Kulasinghe", age: 30}, + {id: 1232, fname: "Khiana", lname: "Jayasoma", age: 2} + ]; - Employee[] records = from var {fname, lname, age} in entities select {fname, lname, age}; + EmployeeQT[] records = from var {fname, lname, age} in entities + select {fname, lname, age}; var lname = 5; boolean testPassed = true; - Employee e = records[0]; + EmployeeQT e = records[0]; testPassed = testPassed && e.fname == "Sameera" && e.lname == "Jayasoma" && e.age == 30; e = records[1]; testPassed = testPassed && e.fname == "Asanthi" && e.lname == "Kulasinghe" && e.age == 30; @@ -371,7 +374,7 @@ function testSimpleSelectQueryWithTable() { assertEquality((table [{"id":1234},{"id":4567}]).toString(), t2.toString()); } -type User record { +type UserQuery record { readonly int id; string firstName; string lastName; @@ -379,10 +382,10 @@ type User record { }; function testQueryConstructingTableWithVar() returns error? { - User u1 = {id: 1, firstName: "John", lastName: "Doe", age: 25}; - User u2 = {id: 2, firstName: "Anne", lastName: "Frank", age: 30}; + UserQuery u1 = {id: 1, firstName: "John", lastName: "Doe", age: 25}; + UserQuery u2 = {id: 2, firstName: "Anne", lastName: "Frank", age: 30}; - table key(id) users = table []; + table key(id) users = table []; users.add(u1); users.add(u2); @@ -390,16 +393,16 @@ function testQueryConstructingTableWithVar() returns error? { where user.age > 21 && user.age < 60 select {user}; - assertEquality(true, result1 is table key(user)); + assertEquality(true, result1 is table key(user)); assertEquality({"user": u1}, result1.get(u1)); - User[] userList = [u1, u2]; + UserQuery[] userList = [u1, u2]; var result2 = check table key(user) from var user in userList where user.age > 21 && user.age < 60 select {user}; - assertEquality(true, result2 is table key(user)); + assertEquality(true, result2 is table key(user)); assertEquality({"user": u1}, result2.get(u1)); } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test.bal b/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test.bal index 2cc46d143d5b..7dd15e0b444d 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test.bal @@ -836,7 +836,7 @@ function testUnreachabilityWithIfElseStmts6() { int _ = 10; } else if e == 20 { int _ = 20; - } else if e == 10 { + } else { never _ = e; // unreachable code } } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test2.bal b/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test2.bal index a5e704049377..1861beac7c92 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test2.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/reachability-analysis/unreachability_test2.bal @@ -422,12 +422,12 @@ function testUnreachabilityWithIfStmtWithEqualityExpr14() { function testUnreachabilityWithIfStmtWithEqualityExpr15() { True t = true; - False f = false; + True f = true; if f == t { - return; // unreachable code + return; } else if t == t { - string _ = "Ballerina"; + string _ = "Ballerina"; // unreachable code } else { string _ = "Ballerina"; // unreachable code } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/record/readonly_record_fields.bal b/tests/jballerina-unit-test/src/test/resources/test-src/record/readonly_record_fields.bal index 2912906e6806..2d97b86a3e54 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/record/readonly_record_fields.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/record/readonly_record_fields.bal @@ -232,40 +232,40 @@ function testReadOnlyFieldWithDefaultValue() { assertEquality("cannot update 'readonly' field 'id' in record of type 'Identifier'", err.detail()["message"]); } -type Foo record {| +type FooH record {| string name; int id; float...; |}; -type Bar record {| +type BarH record {| readonly string name; readonly int id; |}; -type EmptyClosedRecord record {| +type EmptyClosedRecordH record {| |}; function testTypeReadOnlyFlagForAllReadOnlyFields() { - Bar st = { + BarH st = { name: "Maryam", id: 1234 }; - Foo & readonly pr = st; - assertTrue(pr is Bar); - assertTrue(pr is Bar & readonly); + FooH & readonly pr = st; + assertTrue(pr is BarH); + assertTrue(pr is BarH & readonly); assertEquality("Maryam", pr.name); assertEquality(1234, pr.id); readonly rd = st; - assertTrue(rd is Bar); - assertTrue(rd is Bar & readonly); + assertTrue(rd is BarH); + assertTrue(rd is BarH & readonly); - EmptyClosedRecord ecr = {}; + EmptyClosedRecordH ecr = {}; readonly rd2 = ecr; - assertTrue(rd2 is EmptyClosedRecord); - assertTrue(rd2 is EmptyClosedRecord & readonly); + assertTrue(rd2 is EmptyClosedRecordH); + assertTrue(rd2 is EmptyClosedRecordH & readonly); assertTrue(rd2 is record {} & readonly); } @@ -275,19 +275,19 @@ record {| function testTypeReadOnlyFlagForAllReadOnlyFieldsInAnonymousRecord() { readonly rd = modAnonRecord; - assertTrue( rd is record { int x; }); - assertTrue(rd is record { int x; } & readonly); - record { int x; } rec = checkpanic rd; + assertTrue(rd is record {int x;}); + assertTrue(rd is record {int x;} & readonly); + record {int x;} rec = checkpanic rd; assertEquality(2, rec.x); record {| readonly int x = 1; - readonly Bar y; + readonly BarH y; |} localAnonRecord = {y: {name: "Amy", id: 1001}}; readonly rd2 = localAnonRecord; - assertTrue( rd2 is record {| int x; Bar y; |}); - assertTrue(rd2 is record { int x; Bar y; } & readonly); - var rec2 = checkpanic rd2; + assertTrue(rd2 is record {|int x; BarH y;|}); + assertTrue(rd2 is record {int x; BarH y;} & readonly); + var rec2 = checkpanic rd2; assertEquality(1, rec2.x); assertEquality("Amy", rec2.y.name); assertEquality(1001, rec2.y.id); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/Ballerina.toml b/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/Ballerina.toml index b7ec0186ad78..bbccc659c584 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/Ballerina.toml +++ b/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/Ballerina.toml @@ -1,4 +1,4 @@ [package] org = "testorg" -name = "recordproject" +name = "closedrecordproject" version = "1.0.0" diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/closed_record_equivalency.bal b/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/closed_record_equivalency.bal index 6cb5e6e63325..2619d1a62c10 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/closed_record_equivalency.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/record/record_project_closed_rec_equiv/closed_record_equivalency.bal @@ -14,10 +14,10 @@ // specific language governing permissions and limitations // under the License. -import recordproject.eq; -import recordproject.eq2; -import recordproject.req; -import recordproject.req2; +import closedrecordproject.eq; +import closedrecordproject.eq2; +import closedrecordproject.req; +import closedrecordproject.req2; public type person1 record {| int age = 0; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_3.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_3.bal index 74bff266e75a..6ed8c273e634 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_3.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_3.bal @@ -260,7 +260,7 @@ function test20(int|string x) { } if x is int && !(x !is int) { - int _ = x; // Type not narrowed. issue #34965 + int _ = x; // OK } else { string _ = x; // Type not narrowed. issue #34965 } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_4.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_4.bal index 3e95ff0b3554..a6c5ec78e343 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_4.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/ifelse/test_type_guard_type_narrow_4.bal @@ -18,18 +18,18 @@ function test1(2|"foo" x) { if x is int && x == 2 { 2 _ = x; // OK } else { - "foo" _ = x; // Type not narrowed. issue #34965 + "foo" _ = x; // OK } if x is 2 && x == 2 { 2 _ = x; // OK } else { - "foo" _ = x; // Type not narrowed. issue #34965 + "foo" _ = x; // OK } if x == 2 && x == 2 { 2 _ = x; // OK } else { - "foo" _ = x; // Type not narrowed. issue #34965 + "foo" _ = x; // OK } } diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern-with-rest-match-pattern.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern-with-rest-match-pattern.bal index 50ee838ae2d9..55982e369681 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern-with-rest-match-pattern.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern-with-rest-match-pattern.bal @@ -221,10 +221,11 @@ function testListMatchPatternWithRestPattern12() { assertEquals(false, a5[2]); } -class FooObj { +class FooObjLMP { public string s; public float f; public byte b; + function init(string s, float f, byte b) { self.s = s; self.f = f; @@ -232,9 +233,10 @@ class FooObj { } } -class BarObj { +class BarObjLMP { public boolean b; public int i; + function init(boolean b, int i) { self.b = b; self.i = i; @@ -242,36 +244,51 @@ class BarObj { } function testListMatchPatternWithRestPattern13() { - FooObj fooObj1 = new ("Fooo", 3.7, 23); - BarObj barObj1 = new (true, 56); - FooObj fooObj2 = new ("Foo2", 10.2, 30); - BarObj barObj2 = new (false, 56); - BarObj barObj3 = new (true, 58); + FooObjLMP fooObj1 = new ("Fooo", 3.7, 23); + BarObjLMP barObj1 = new (true, 56); + FooObjLMP fooObj2 = new ("Foo2", 10.2, 30); + BarObjLMP barObj2 = new (false, 56); + BarObjLMP barObj3 = new (true, 58); string matched = "Not Matched"; - [[string, [error, map, int, (FooObj|BarObj)...], Bar, (byte|float)...], string, boolean...] t2 = - [["Ballerina", [error("Error", detail1= 12, detail2= true), - {firstName: "John", lastName: "Damon"}, 12, fooObj1, barObj1], {id: 34, flag: true}, 10.5, 20], - "A", true, false]; + [[string, [error, map, int, (FooObjLMP|BarObjLMP)...], Bar, (byte|float)...], string, boolean...] t2 = + [ + [ + "Ballerina", + [ + error("Error", detail1 = 12, detail2 = true), + {firstName: "John", lastName: "Damon"}, + 12, + fooObj1, + barObj1 + ], + {id: 34, flag: true}, + 10.5, + 20 + ], + "A", + true, + false + ]; string a1; error a2; - [map, int, (FooObj|BarObj)...] a3; + [map, int, (FooObjLMP|BarObjLMP)...] a3; [Bar, (byte|float)...] a4; [string, boolean...] a5; map a6; - [int, (FooObj|BarObj)...] a7; + [int, (FooObjLMP|BarObjLMP)...] a7; string b1; error b2; - [map, int, (FooObj|BarObj)...] b3; + [map, int, (FooObjLMP|BarObjLMP)...] b3; [Bar, (byte|float)...] b4; [string, boolean...] b5; map b6; - [int, (FooObj|BarObj)...] b7; + [int, (FooObjLMP|BarObjLMP)...] b7; match t2 { - [[var g1, [var g2, ... var g3], ...var g4], ...var g5] => { + [[var g1, [var g2, ...var g3], ...var g4], ...var g5] => { matched = "Matched1"; a1 = g1; a2 = g2; @@ -286,9 +303,24 @@ function testListMatchPatternWithRestPattern13() { } } - [[g1, g2, ...g3], [...g5], ...g4] = [["Hello", error("Transaction Error"), [{primary: "Blue", - secondary: "Green"}, 1000, barObj2, fooObj2, barObj3]], [["World", true, false, true, false]], - [{id: 40, flag: true}, 0x5, 0x7, 20.25, 0x8]]; + [[g1, g2, ...g3], [...g5], ...g4] = [ + [ + "Hello", + error("Transaction Error"), + [ + { + primary: "Blue", + secondary: "Green" + }, + 1000, + barObj2, + fooObj2, + barObj3 + ] + ], + [["World", true, false, true, false]], + [{id: 40, flag: true}, 0x5, 0x7, 20.25, 0x8] + ]; b1 = g1; b2 = g2; b3 = g3; @@ -323,16 +355,16 @@ function testListMatchPatternWithRestPattern13() { assertEquals("Blue", b3[0]["primary"]); assertEquals("Green", b3[0]["secondary"]); assertEquals(1000, b3[1]); - assertEquals(true, b3[2] is BarObj); - assertEquals(false, (b3[2]).b); - assertEquals(56, (b3[2]).i); - assertEquals(true, b3[3] is FooObj); - assertEquals("Foo2", (b3[3]).s); - assertEquals(10.2, (b3[3]).f); - assertEquals(30, (b3[3]).b); - assertEquals(true, b3[4] is BarObj); - assertEquals(true, (b3[4]).b); - assertEquals(58, (b3[4]).i); + assertEquals(true, b3[2] is BarObjLMP); + assertEquals(false, (b3[2]).b); + assertEquals(56, (b3[2]).i); + assertEquals(true, b3[3] is FooObjLMP); + assertEquals("Foo2", (b3[3]).s); + assertEquals(10.2, (b3[3]).f); + assertEquals(30, (b3[3]).b); + assertEquals(true, b3[4] is BarObjLMP); + assertEquals(true, (b3[4]).b); + assertEquals(58, (b3[4]).i); assertEquals(5, b5.length()); assertEquals("World", b5[0]); assertEquals(true, b5[1]); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern.bal index 0395cd3756c2..32bb925e047d 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/list-match-pattern.bal @@ -504,35 +504,35 @@ function testListMatchPattern18() { assertEquals("Default" ,listMatchPattern18(a5)); } -type FooRec record { +type FooRecLMP record { string s; int i; float f; }; -type BarRec record { +type BarRecLMP record { byte b; - FooRec f; + FooRecLMP f; }; function listMatchPattern19(any a) returns string { match a { - [var i, var s] if i is FooRec && s is BarRec => { + [var i, var s] if i is FooRecLMP && s is BarRecLMP => { return "Matched with FooRec and BarRec : " + i.toString() + " , " + s.toString(); } - [var i, var s] if i is FooRec && s is float => { + [var i, var s] if i is FooRecLMP && s is float => { return "Matched with FooRec and float : " + i.toString() + " , " + s.toString(); } - [var i, var s] if i is BarRec && s is FooRec => { + [var i, var s] if i is BarRecLMP && s is FooRecLMP => { return "Matched with BarRec and FooRec : " + i.toString() + " , " + s.toString(); } - [var i, var s] if i is BarRec && s is int => { + [var i, var s] if i is BarRecLMP && s is int => { return "Matched with BarRec and int : " + i.toString() + " , " + s.toString(); } - [var i, var s] if i is float && s is FooRec => { + [var i, var s] if i is float && s is FooRecLMP => { return "Matched with float and FooRec : " + i.toString() + " , " + s.toString(); } - [var i, var s] if i is int && s is BarRec => { + [var i, var s] if i is int && s is BarRecLMP => { return "Matched with int and BarRec : " + i.toString() + " , " + s.toString(); } } @@ -541,34 +541,34 @@ function listMatchPattern19(any a) returns string { } function testListMatchPattern19() { - FooRec fooRec1 = {s: "S", i: 23, f: 5.6}; - BarRec barRec1 = {b: 12, f: fooRec1}; - - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a1 = [fooRec1, barRec1]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a2 = [fooRec1, 4.5]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a3 = [barRec1, fooRec1]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a4 = [barRec1, 543]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a5 = [5.2, fooRec1]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a6 = [15, barRec1]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a7 = [65, 7.4]; - [int|FooRec, float|BarRec] | [float|BarRec, int|FooRec] a8 = [3.6, 42]; + FooRecLMP fooRec1 = {s: "S", i: 23, f: 5.6}; + BarRecLMP barRec1 = {b: 12, f: fooRec1}; + + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a1 = [fooRec1, barRec1]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a2 = [fooRec1, 4.5]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a3 = [barRec1, fooRec1]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a4 = [barRec1, 543]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a5 = [5.2, fooRec1]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a6 = [15, barRec1]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a7 = [65, 7.4]; + [int|FooRecLMP, float|BarRecLMP]|[float|BarRecLMP, int|FooRecLMP] a8 = [3.6, 42]; assertEquals("Matched with FooRec and BarRec : {\"s\":\"S\",\"i\":23,\"f\":5.6} , " + "{\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}}", listMatchPattern19(a1)); - assertEquals("Matched with FooRec and float : {\"s\":\"S\",\"i\":23,\"f\":5.6} , 4.5" ,listMatchPattern19(a2)); + assertEquals("Matched with FooRec and float : {\"s\":\"S\",\"i\":23,\"f\":5.6} , 4.5", listMatchPattern19(a2)); assertEquals("Matched with BarRec and FooRec : {\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}} , " + - "{\"s\":\"S\",\"i\":23,\"f\":5.6}" ,listMatchPattern19(a3)); - assertEquals("Matched with BarRec and int : {\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}} , 543" , - listMatchPattern19(a4)); - assertEquals("Matched with float and FooRec : 5.2 , {\"s\":\"S\",\"i\":23,\"f\":5.6}" ,listMatchPattern19(a5)); - assertEquals("Matched with int and BarRec : 15 , {\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}}" , - listMatchPattern19(a6)); - assertEquals("Default" ,listMatchPattern19(a7)); - assertEquals("Default" ,listMatchPattern19(a8)); + "{\"s\":\"S\",\"i\":23,\"f\":5.6}", listMatchPattern19(a3)); + assertEquals("Matched with BarRec and int : {\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}} , 543", + listMatchPattern19(a4)); + assertEquals("Matched with float and FooRec : 5.2 , {\"s\":\"S\",\"i\":23,\"f\":5.6}", listMatchPattern19(a5)); + assertEquals("Matched with int and BarRec : 15 , {\"b\":12,\"f\":{\"s\":\"S\",\"i\":23,\"f\":5.6}}", + listMatchPattern19(a6)); + assertEquals("Default", listMatchPattern19(a7)); + assertEquals("Default", listMatchPattern19(a8)); } function listMatchPattern20() returns string { - [boolean, string] | [int, string, decimal] v = [1, "A", 1.1d]; + [boolean, string]|[int, string, decimal] v = [1, "A", 1.1d]; match v { [var i, ...var s] => { return "i: " + i.toString() + " s: " + s.toString(); @@ -699,7 +699,7 @@ function testListMatchPatternWithWildCard() { result = "Matched"; } _ => { - result = "Default"; + result = "Default"; } } assertEquals("Default", result); @@ -711,7 +711,7 @@ function testListMatchPatternWithWildCard() { result = "Matched"; } _ => { - result = "Default"; + result = "Default"; } } assertEquals("Not Matched", result); @@ -719,10 +719,10 @@ function testListMatchPatternWithWildCard() { function testListMatchPatternWithArrayAndAnydataIntersection() { int[] x = [1, 2, 3]; - assertEquals(x, listMatchPattern28( [x])); + assertEquals(x, listMatchPattern28([x])); anydata[] y = [["hello", "world"]]; assertEquals(["hello", "world"], listMatchPattern28(y)); - assertEquals("other", listMatchPattern28( [["hello", "world"], 1, 2])); + assertEquals("other", listMatchPattern28([["hello", "world"], 1, 2])); assertEquals("other", listMatchPattern28("hello")); } @@ -745,12 +745,12 @@ function testListMatchPattern29() { assertEquals((), listMatchPattern29(1)); } -type Rec record {| +type RecLMP record {| int|float a; |}; function testListMatchPattern30() { - [int, Rec|string] a1 = [12, {a: 1}]; + [int, RecLMP|string] a1 = [12, {a: 1}]; string result = ""; match a1 { @@ -781,7 +781,7 @@ function testListMatchPattern30() { } assertEquals("Pattern3", result); - [int, Rec|string...] a2 = [12, {a: 1}]; + [int, RecLMP|string...] a2 = [12, {a: 1}]; result = ""; match a2 { @@ -797,7 +797,7 @@ function testListMatchPattern30() { } assertEquals("Pattern3", result); - [int, string, Rec|string...] a3 = [12, "C", {a: 1.5}]; + [int, string, RecLMP|string...] a3 = [12, "C", {a: 1.5}]; result = ""; match a3 { @@ -813,7 +813,7 @@ function testListMatchPattern30() { } assertEquals("Pattern2", result); - [Rec|string...] a4 = [{a: 1}, {a: 2}, {a: 3}]; + [RecLMP|string...] a4 = [{a: 1}, {a: 2}, {a: 3}]; result = ""; match a4 { @@ -838,8 +838,8 @@ function testListMatchPattern30() { } assertEquals("Pattern2", result); - error err1 = error("Error One", data= [{b: 5}, 12]); - [error, Rec|string...] a5 = [err1, {a: 2}, {a: 3}]; + error err1 = error("Error One", data = [{b: 5}, 12]); + [error, RecLMP|string...] a5 = [err1, {a: 2}, {a: 3}]; result = ""; match a5 { @@ -853,21 +853,22 @@ function testListMatchPattern30() { assertEquals("Pattern2", result); } -type T readonly & S; -type S [INT, int]|[STRING, string]; +type TLMP readonly & SLMP; + +type SLMP [INT, int]|[STRING, string]; const INT = 1; const STRING = 2; function testListMatchPattern31() { - T t1 = [STRING, "hello"]; - T t2 = [INT, 1234]; + TLMP t1 = [STRING, "hello"]; + TLMP t2 = [INT, 1234]; assertEquals(["hello", ()], listMatchPattern31(t1)); assertEquals([(), 1234], listMatchPattern31(t2)); } -function listMatchPattern31(T t) returns [string?, int?] { +function listMatchPattern31(TLMP t) returns [string?, int?] { string? s = (); int? i = (); @@ -884,14 +885,14 @@ function listMatchPattern31(T t) returns [string?, int?] { } function testListMatchPattern32() { - T t1 = [STRING, "hello"]; - T t2 = [INT, 1234]; + TLMP t1 = [STRING, "hello"]; + TLMP t2 = [INT, 1234]; assertEquals("hello", listMatchPattern32(t1)); assertEquals(1234, listMatchPattern32(t2)); } -function listMatchPattern32(T t) returns string|int { +function listMatchPattern32(TLMP t) returns string|int { string|int s; match t { @@ -903,19 +904,19 @@ function listMatchPattern32(T t) returns string|int { return s; } -type T2 readonly & ([1, string]|[2, string]|[3, string]); +type T2LMP readonly & ([1, string]|[2, string]|[3, string]); function testListMatchPattern33() { - T2 t1 = [1, "hello"]; - T2 t2 = [2, "1234"]; - T2 t3 = [3, "abcd"]; + T2LMP t1 = [1, "hello"]; + T2LMP t2 = [2, "1234"]; + T2LMP t3 = [3, "abcd"]; assertEquals("hello", listMatchPattern33(t1)); assertEquals("1234", listMatchPattern33(t2)); assertEquals("abcd", listMatchPattern33(t3)); } -function listMatchPattern33(T2 t) returns string { +function listMatchPattern33(T2LMP t) returns string { string s; match t { @@ -927,18 +928,19 @@ function listMatchPattern33(T2 t) returns string { return s; } -type T3 readonly & S3; -type S3 string[2]|int[2]; +type T3LMP readonly & S3LMP; + +type S3LMP string[2]|int[2]; function testListMatchPattern34() { - T3 t1 = ["1", "hello"]; - T3 t2 = [2, 1234]; + T3LMP t1 = ["1", "hello"]; + T3LMP t2 = [2, 1234]; assertEquals("hello", listMatchPattern34(t1)); assertEquals(1234, listMatchPattern34(t2)); } -function listMatchPattern34(T3 t) returns string|int { +function listMatchPattern34(T3LMP t) returns string|int { string|int s = 10; match t { @@ -950,22 +952,22 @@ function listMatchPattern34(T3 t) returns string|int { return s; } -public type T4 ["list", T4[]]|"int"; +public type T4LMP ["list", T4LMP[]]|"int"; function testListMatchPattern35() { - T4[] t1 = ["int"]; - T4[] t2 = ["int", "int", "int"]; + T4LMP[] t1 = ["int"]; + T4LMP[] t2 = ["int", "int", "int"]; - T4 x1 = ["list", t1]; - T4 x2 = ["list", ["int", "int"]]; - T4 x3 = ["list", t2]; + T4LMP x1 = ["list", t1]; + T4LMP x2 = ["list", ["int", "int"]]; + T4LMP x3 = ["list", t2]; assertEquals(listMatchPattern35(x1, t1), "match 4"); assertEquals(listMatchPattern35("int", ()), "match 1"); assertEquals(listMatchPattern35(x2, ()), "match 2"); assertEquals(listMatchPattern35(x3, t2), "match 4"); } -function listMatchPattern35(T4 x, T4[]? t) returns string? { +function listMatchPattern35(T4LMP x, T4LMP[]? t) returns string? { match x { "int" => { return "match 1"; @@ -987,19 +989,19 @@ function listMatchPattern35(T4 x, T4[]? t) returns string? { } function testListMatchPattern36() { - T4[] t1 = ["int"]; - T4[] t2 = ["int", "int", "int"]; + T4LMP[] t1 = ["int"]; + T4LMP[] t2 = ["int", "int", "int"]; - T4 x1 = ["list", t1]; - T4 x2 = ["list", ["int", "int"]]; - T4 x3 = ["list", t2]; + T4LMP x1 = ["list", t1]; + T4LMP x2 = ["list", ["int", "int"]]; + T4LMP x3 = ["list", t2]; assertEquals(listMatchPattern36(x1, t1), "match 4"); assertEquals(listMatchPattern36("int", ()), "match 1"); assertEquals(listMatchPattern36(x2, ()), "match 2"); assertEquals(listMatchPattern36(x3, t2), "match 4"); } -function listMatchPattern36((T4|anydata)? x, T4[]? t) returns string? { +function listMatchPattern36((T4LMP|anydata)? x, T4LMP[]? t) returns string? { string? a = (); match x { "int" => { @@ -1018,16 +1020,17 @@ function listMatchPattern36((T4|anydata)? x, T4[]? t) returns string? { } } -public type T5 ["array", T6]|["cell", T6, string]; -public type T6 ["|", T6]|"int"; +public type T5LMP ["array", T6LMP]|["cell", T6LMP, string]; + +public type T6LMP ["|", T6LMP]|"int"; function testListMatchPattern37() { - T6 t1 = ["|", ["|", "int"]]; - T6 t2 = "int"; - T5 x1 = ["cell", t1, "inf"]; - T5 x2 = ["array", t1]; - T5 x3 = ["cell", t2, "inf1"]; - T5 x4 = ["array", t2]; + T6LMP t1 = ["|", ["|", "int"]]; + T6LMP t2 = "int"; + T5LMP x1 = ["cell", t1, "inf"]; + T5LMP x2 = ["array", t1]; + T5LMP x3 = ["cell", t2, "inf1"]; + T5LMP x4 = ["array", t2]; assertEquals(listMatchPattern37(x1, t1, "inf"), "match 2"); assertEquals(listMatchPattern37(x2, t1, ()), "match 4"); @@ -1035,7 +1038,7 @@ function testListMatchPattern37() { assertEquals(listMatchPattern37(x4, t2, ()), "match 4"); } -function listMatchPattern37(T5 x, T6? t, string? s) returns string { +function listMatchPattern37(T5LMP x, T6LMP? t, string? s) returns string { match x { ["cell", "int", "inf1"] => { return "match 1"; @@ -1059,12 +1062,12 @@ function listMatchPattern37(T5 x, T6? t, string? s) returns string { } function testListMatchPattern38() { - T6 t1 = ["|", ["|", "int"]]; - T6 t2 = "int"; - T5 x1 = ["cell", t1, "inf"]; - T5 x2 = ["array", t1]; - T5 x3 = ["cell", t2, "inf1"]; - T5 x4 = ["array", t2]; + T6LMP t1 = ["|", ["|", "int"]]; + T6LMP t2 = "int"; + T5LMP x1 = ["cell", t1, "inf"]; + T5LMP x2 = ["array", t1]; + T5LMP x3 = ["cell", t2, "inf1"]; + T5LMP x4 = ["array", t2]; assertEquals(listMatchPattern38(x1, t1, "inf"), "match 2"); assertEquals(listMatchPattern38(x2, t1, ()), "match 4"); @@ -1072,7 +1075,7 @@ function testListMatchPattern38() { assertEquals(listMatchPattern38(x4, t2, ()), "match 4"); } -function listMatchPattern38((anydata|T5)? x, T6? t, string? s) returns string? { +function listMatchPattern38((anydata|T5LMP)? x, T6LMP? t, string? s) returns string? { match x { ["cell", "int", "inf1"] => { return "match 1"; @@ -1092,40 +1095,40 @@ function listMatchPattern38((anydata|T5)? x, T6? t, string? s) returns string? { } } -public type T7 ["array", T6]|["cell", T6]; +public type T7LMP ["array", T6LMP]|["cell", T6LMP]; function testListMatchPattern39() { - T6 y1 = "int"; - T6 y2 = ["|", "int"]; + T6LMP y1 = "int"; + T6LMP y2 = ["|", "int"]; - T7 x1 = ["cell", y1]; - T7 x2 = ["array", y1]; - T7 x3 = ["cell", y2]; + T7LMP x1 = ["cell", y1]; + T7LMP x2 = ["array", y1]; + T7LMP x3 = ["cell", y2]; assertEquals(listMatchPattern39(x1, y1), "match 3"); assertEquals(listMatchPattern39(x2, y1), "match 2"); assertEquals(listMatchPattern39(x3, y2), "match 3"); } -function listMatchPattern39(T7 x, T6 y) returns string { +function listMatchPattern39(T7LMP x, T6LMP y) returns string { match x { ["list", var _] => { - T6 _ = x[1]; - T6 a = x[1]; + T6LMP _ = x[1]; + T6LMP a = x[1]; assertEquals(a, y); assertEquals(x[0], "list"); return "match 1"; } ["array", var _] => { - T6 _ = x[1]; - T6 a = x[1]; + T6LMP _ = x[1]; + T6LMP a = x[1]; assertEquals(a, y); assertEquals(x[0], "array"); return "match 2"; } ["cell", var _] => { - T6 _ = x[1]; - T6 a = x[1]; + T6LMP _ = x[1]; + T6LMP a = x[1]; assertEquals(a, y); assertEquals(x[0], "cell"); return "match 3"; @@ -1136,19 +1139,19 @@ function listMatchPattern39(T7 x, T6 y) returns string { } } -public type T8 ["list", T8, T8[]]|["list", T8[]]|"int"; +public type T8LMP ["list", T8LMP, T8LMP[]]|["list", T8LMP[]]|"int"; function testListMatchPattern40() { - T8 t1 = "int"; - T8[] t2 = ["int", "int", "int"]; - T8[] t3 = [t1]; - T8 t4 = ["list", ["int", "int", "int"]]; + T8LMP t1 = "int"; + T8LMP[] t2 = ["int", "int", "int"]; + T8LMP[] t3 = [t1]; + T8LMP t4 = ["list", ["int", "int", "int"]]; - T8 x1 = ["list", t3]; - T8 x2 = ["list", ["int", "int"]]; - T8 x3 = ["list", t2]; - T8 x4 = ["list", "int", t2]; - T8 x5 = ["list", t4, t3]; + T8LMP x1 = ["list", t3]; + T8LMP x2 = ["list", ["int", "int"]]; + T8LMP x3 = ["list", t2]; + T8LMP x4 = ["list", "int", t2]; + T8LMP x5 = ["list", t4, t3]; assertEquals(listMatchPattern40(x1, (), t3, ()), "match 4"); assertEquals(listMatchPattern40("int", (), (), ()), "match 1"); @@ -1158,7 +1161,7 @@ function testListMatchPattern40() { assertEquals(listMatchPattern40(x5, t4, t3, ()), "match 6"); } -function listMatchPattern40(T8 x, T8? t1, T8[]? t2, T8? t3) returns string? { +function listMatchPattern40(T8LMP x, T8LMP? t1, T8LMP[]? t2, T8LMP? t3) returns string? { match x { "int" => { return "match 1"; @@ -1177,7 +1180,7 @@ function listMatchPattern40(T8 x, T8? t1, T8[]? t2, T8? t3) returns string? { return "match 5"; } ["list", var y, var z] => { - T8 _ = z[0]; + T8LMP _ = z[0]; assertEquals(y, t1); assertEquals(z, t2); return "match 6"; @@ -1188,15 +1191,15 @@ function listMatchPattern40(T8 x, T8? t1, T8[]? t2, T8? t3) returns string? { } } -public type T9 ["array", T9]|["cell", T6]|["array", T6]|[string, int]; +public type T9LMP ["array", T9LMP]|["cell", T6LMP]|["array", T6LMP]|[string, int]; function testListMatchPattern41() { - T9 x1 = ["cell", "int"]; - T9 x2 = ["array", ["|", "int"]]; - T9 x3 = ["cell", ["|", "int"]]; - T9 x4 = ["string 1", 1]; - T9 x5 = ["array", x4]; - T9 x6 = ["string 2", 1]; + T9LMP x1 = ["cell", "int"]; + T9LMP x2 = ["array", ["|", "int"]]; + T9LMP x3 = ["cell", ["|", "int"]]; + T9LMP x4 = ["string 1", 1]; + T9LMP x5 = ["array", x4]; + T9LMP x6 = ["string 2", 1]; assertEquals(listMatchPattern41(x1), "match 1"); assertEquals(listMatchPattern41(x2), "match 4"); @@ -1206,7 +1209,7 @@ function testListMatchPattern41() { assertEquals(listMatchPattern41(x6), "match 6"); } -function listMatchPattern41(T9 x) returns string { +function listMatchPattern41(T9LMP x) returns string { match x { ["cell", "int"] => { return "match 1"; @@ -1229,14 +1232,14 @@ function listMatchPattern41(T9 x) returns string { } } -public type T10 [string, decimal, string]|[string, boolean...]|[int...]|[boolean]; +public type T10LMP [string, decimal, string]|[string, boolean...]|[int...]|[boolean]; function testListMatchPattern42() { - T10 x1 = ["string", 1d, "string"]; - T10 x2 = ["string", true, true, true, true, true, true]; - T10 x3 = [1, 1, 1, 1]; - T10 x4 = [true]; - T10 x5 = ["string", true]; + T10LMP x1 = ["string", 1d, "string"]; + T10LMP x2 = ["string", true, true, true, true, true, true]; + T10LMP x3 = [1, 1, 1, 1]; + T10LMP x4 = [true]; + T10LMP x5 = ["string", true]; assertEquals(listMatchPattern42(x1, ["string", 1d, "string"]), "match 1"); assertEquals(listMatchPattern42(x2, ["string", true, true, true, true, [true, true]]), "match 3"); @@ -1245,7 +1248,7 @@ function testListMatchPattern42() { assertEquals(listMatchPattern42(x5, ["string", true]), "match 5"); } -function listMatchPattern42(T10 t, anydata a) returns string { +function listMatchPattern42(T10LMP t, anydata a) returns string { match t { [var x, var y, var z] => { assertEquals([x, y, z], a); @@ -1270,25 +1273,25 @@ function listMatchPattern42(T10 t, anydata a) returns string { } } -public type T11 [int, T11, T11...]|[T11...]|["int"]; +public type T11LMP [int, T11LMP, T11LMP...]|[T11LMP...]|["int"]; public function testListMatchPattern43() { - T11[] t1 = [["int"], ["int"], ["int"]]; - T11 x1 = [1, ["int"], ["int"]]; - T11 x2 = [1, ["int"], ["int"], ["int"], ["int"], ["int"], ["int"], ["int"]]; - T11 x3 = [["int"], ["int"], ["int"], ["int"]]; - T11 x4 = [["int"]]; - T11 x5 = [t1, ["int"]]; + T11LMP[] t1 = [["int"], ["int"], ["int"]]; + T11LMP x1 = [1, ["int"], ["int"]]; + T11LMP x2 = [1, ["int"], ["int"], ["int"], ["int"], ["int"], ["int"], ["int"]]; + T11LMP x3 = [["int"], ["int"], ["int"], ["int"]]; + T11LMP x4 = [["int"]]; + T11LMP x5 = [t1, ["int"]]; assertEquals(listMatchPattern43(x1, [1, ["int"], ["int"]]), "match 1"); assertEquals(listMatchPattern43(x2, - [1, ["int"], ["int"], ["int"], ["int"], [["int"], ["int"], ["int"]]]), "match 3"); + [1, ["int"], ["int"], ["int"], ["int"], [["int"], ["int"], ["int"]]]), "match 3"); assertEquals(listMatchPattern43(x3, [["int"], ["int"], ["int"], [["int"]]]), "match 4"); assertEquals(listMatchPattern43(x4, [["int"]]), "match 2"); assertEquals(listMatchPattern43(x5, [t1, ["int"]]), "match 5"); } -function listMatchPattern43(T11 t, anydata a) returns string { +function listMatchPattern43(T11LMP t, anydata a) returns string { match t { [var x, var y, var z] => { assertEquals([x, y, z], a); @@ -1313,23 +1316,24 @@ function listMatchPattern43(T11 t, anydata a) returns string { } } -public type T12 [int, T12[], T12...]|[T12[]...]|"int"; -public type T13 [int, T13, T13, T13[]...]|[T13...]|"int"; +public type T12LMP [int, T12LMP[], T12LMP...]|[T12LMP[]...]|"int"; + +public type T13LMP [int, T13LMP, T13LMP, T13LMP[]...]|[T13LMP...]|"int"; public function testListMatchPattern44() { - T12[] t1 = ["int", "int", "int"]; - T12 x1 = [1, t1, "int", "int"]; - T12 x2 = [1, t1, "int", "int", "int", "int", "int", "int", "int"]; - T12 x3 = [t1, t1, t1, t1, t1]; - T12 x4 = [t1]; - T12 x5 = [t1, t1]; - - T13[] t2 = ["int", "int", "int"]; - T13 y1 = [1, "int", "int", t2, t2]; - T13 y2 = [1, "int", "int", t2, t2, t2, t2, t2, t2, t2]; - T13 y3 = [t2, t2, t2, t2, t2, t2]; - T13 y4 = [t2]; - T13 y5 = [t2, t2]; + T12LMP[] t1 = ["int", "int", "int"]; + T12LMP x1 = [1, t1, "int", "int"]; + T12LMP x2 = [1, t1, "int", "int", "int", "int", "int", "int", "int"]; + T12LMP x3 = [t1, t1, t1, t1, t1]; + T12LMP x4 = [t1]; + T12LMP x5 = [t1, t1]; + + T13LMP[] t2 = ["int", "int", "int"]; + T13LMP y1 = [1, "int", "int", t2, t2]; + T13LMP y2 = [1, "int", "int", t2, t2, t2, t2, t2, t2, t2]; + T13LMP y3 = [t2, t2, t2, t2, t2, t2]; + T13LMP y4 = [t2]; + T13LMP y5 = [t2, t2]; assertEquals(listMatchPattern44(x1, [1, t1, "int", "int"]), "match 1"); assertEquals(listMatchPattern44(x2, [1, t1, "int", "int", "int", "int", ["int", "int", "int"]]), "match 3"); @@ -1344,8 +1348,8 @@ public function testListMatchPattern44() { assertEquals(listMatchPattern44(y5, [t2, t2]), "match 5"); } -function listMatchPattern44(T12|T13 t, anydata a) returns string? { - if t is T12 { +function listMatchPattern44(T12LMP|T13LMP t, anydata a) returns string? { + if t is T12LMP { match t { [var p, var x, var y, var z] => { assertEquals([p, x, y, z], a); @@ -1394,18 +1398,19 @@ function listMatchPattern44(T12|T13 t, anydata a) returns string? { } } -public type T14 [string]|[int, string]|[int, int, string]; -public type T15 [int]|[T15, T15]|[T15[], T15[], T15[]]; +public type T14LMP [string]|[int, string]|[int, int, string]; + +public type T15LMP [int]|[T15LMP, T15LMP]|[T15LMP[], T15LMP[], T15LMP[]]; public function testListMatchPattern45() { - T14 x1 = ["string"]; - T14 x2 = [1, "string"]; - T14 x3 = [1, 1, "string"]; + T14LMP x1 = ["string"]; + T14LMP x2 = [1, "string"]; + T14LMP x3 = [1, 1, "string"]; - T15 y1 = [1]; - T15[] t2 = [y1, y1]; - T15 y2 = [y1, y1]; - T15 y3 = [t2, t2, t2]; + T15LMP y1 = [1]; + T15LMP[] t2 = [y1, y1]; + T15LMP y2 = [y1, y1]; + T15LMP y3 = [t2, t2, t2]; assertEquals(listMatchPattern45(x1, x1), "match 3"); assertEquals(listMatchPattern45(x2, x2), "match 2"); @@ -1416,7 +1421,7 @@ public function testListMatchPattern45() { assertEquals(listMatchPattern45(y3, y3), "match 1"); } -function listMatchPattern45(T14|T15 t, anydata a) returns string? { +function listMatchPattern45(T14LMP|T15LMP t, anydata a) returns string? { match t { [var p, var x, var y, ...var z] => { assertEquals([p, x, y], a); @@ -1436,14 +1441,14 @@ function listMatchPattern45(T14|T15 t, anydata a) returns string? { } } -public type T16 [string, int]; +public type T16LMP [string, int]; public function testListMatchPattern46() { assertEquals(listMatchPattern46(), "string"); } public function listMatchPattern46() returns string { - T16 a = ["string", 1]; + T16LMP a = ["string", 1]; string b; match a { [_, var x] => { @@ -1453,17 +1458,20 @@ public function listMatchPattern46() returns string { return b; } -type Data string|Data[]; -type Data2 ["call", string, Data...]; -type Data3 ["branch", string]; -type Data4 Data2|Data3; +type DataLMP string|DataLMP[]; + +type Data2LMP ["call", string, DataLMP...]; + +type Data3LMP ["branch", string]; + +type Data4LMP Data2LMP|Data3LMP; public function testListMatchPattern47() { assertEquals(listMatchPattern47(["branch", "b.target"]), "match 2"); assertEquals(listMatchPattern47(["call", "add", "1", "2"]), "match 1"); } -function listMatchPattern47(Data4 d) returns string { +function listMatchPattern47(Data4LMP d) returns string { match d { ["call", "add", ...var operands] => { return "match 1"; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/match-stmt-type-narrow.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/match-stmt-type-narrow.bal index fb4d64647048..2c57d02fd94a 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/match-stmt-type-narrow.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/match-stmt-type-narrow.bal @@ -226,19 +226,19 @@ function testMatchClauseWithTypeGuard6() { assertEquals("Int or String", matched); } -type Person record {| +type PersonMtStmt record {| string name; int age; - Address address; + AddressMtStmt address; |}; -type Address record { +type AddressMtStmt record { string street; int houseNo; string city; }; -type AddressWithProvince record { +type AddressWithProvinceMtStmt record { string street; int houseNo; string city; @@ -248,20 +248,20 @@ type AddressWithProvince record { type E 100|200; function testMatchClauseWithTypeGuard7() { - Person person = {name: "John", age: 12, address: {street: "Main Street", houseNo:10, city: "Colombo", + PersonMtStmt person = {name: "John", age: 12, address: {street: "Main Street", houseNo:10, city: "Colombo", "country": "Sri Lanka"}}; string matched = "Not Matched"; int age = 0; string street = ""; - Person newPerson = person; + PersonMtStmt newPerson = person; match person { - {name: var a, ...var b} if a is string && b is record {|int age; AddressWithProvince address;|} => { + {name: var a, ...var b} if a is string && b is record {|int age; AddressWithProvinceMtStmt address;|} => { matched = "Pattern1"; age = b.age; street = b.address.street; } - {name: var a, ...var b} if a is string && b is record {|int age; Address address;|} => { + {name: var a, ...var b} if a is string && b is record {|int age; AddressMtStmt address;|} => { matched = "Pattern2"; age = b.age; street = b.address.street; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/structured_record_match_patterns.bal b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/structured_record_match_patterns.bal index dcdad44551b9..b37e6d5fabab 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/structured_record_match_patterns.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/statements/matchstmt/structured_record_match_patterns.bal @@ -14,14 +14,14 @@ // specific language governing permissions and limitations // under the License. -type Foo record { +type FooSRMP record { string s; int i; float f; }; function testStructuredMatchPatternsBasic1() returns string { - Foo foo = {s: "S", i: 23, f: 5.6}; + FooSRMP foo = {s: "S", i: 23, f: 5.6}; match foo { var {s, i: integer, f} => { @@ -32,14 +32,14 @@ function testStructuredMatchPatternsBasic1() returns string { return "Default"; } -type Bar record { +type BarSRMP record { byte b; - Foo f; + FooSRMP f; }; function testStructuredMatchPatternsBasic2() returns string { - Foo foo = {s: "S", i: 23, f: 5.6}; - Bar bar = {b: 12, f: foo}; + FooSRMP foo = {s: "S", i: 23, f: 5.6}; + BarSRMP bar = {b: 12, f: foo}; match bar { var {b: byteValue, f: {s, i, f}} => { @@ -52,8 +52,8 @@ function testStructuredMatchPatternsBasic2() returns string { } function testStructuredMatchPatternsBasic3() returns string { - Foo foo = {s: "S", i: 23, f: 5.6}; - Bar bar = {b: 12, f: foo}; + FooSRMP foo = {s: "S", i: 23, f: 5.6}; + BarSRMP bar = {b: 12, f: foo}; match bar { var {b, f} => { @@ -65,8 +65,8 @@ function testStructuredMatchPatternsBasic3() returns string { } function testStructuredMatchPatternsBasic4() returns string { - Foo foo = {s: "S", i: 23, f: 5.6}; - Bar bar = {b: 12, f: foo}; + FooSRMP foo = {s: "S", i: 23, f: 5.6}; + BarSRMP bar = {b: 12, f: foo}; match bar { var {a} => { @@ -105,17 +105,17 @@ function testStructuredMatchPatternsBasics5() returns string[] { ClosedFoo3 foo3 = {var1: "Hello", var2: 150, var3: true}; ClosedFoo4 foo4 = {var1: "Hello"}; - ClosedFoo1 | ClosedFoo2 | ClosedFoo3 | ClosedFoo4 a1 = foo1; - ClosedFoo1 | ClosedFoo2 | ClosedFoo3 | ClosedFoo4 a2 = foo2; - ClosedFoo1 | ClosedFoo2 | ClosedFoo3 | ClosedFoo4 a3 = foo3; - ClosedFoo1 | ClosedFoo2 | ClosedFoo3 | ClosedFoo4 a4 = foo4; + ClosedFoo1|ClosedFoo2|ClosedFoo3|ClosedFoo4 a1 = foo1; + ClosedFoo1|ClosedFoo2|ClosedFoo3|ClosedFoo4 a2 = foo2; + ClosedFoo1|ClosedFoo2|ClosedFoo3|ClosedFoo4 a3 = foo3; + ClosedFoo1|ClosedFoo2|ClosedFoo3|ClosedFoo4 a4 = foo4; string[] result = [basicMatch(a1), basicMatch(a2), basicMatch(a3), basicMatch(a4)]; return result; } -function basicMatch(ClosedFoo1 | ClosedFoo2 | ClosedFoo3 | ClosedFoo4 a) returns string { +function basicMatch(ClosedFoo1|ClosedFoo2|ClosedFoo3|ClosedFoo4 a) returns string { match a { var {var1, var2, var3} => { return "Matched with three vars : " + var1.toString() + ", " + @@ -146,16 +146,16 @@ function testStructuredMatchPatternComplex1() returns string[] { ClosedBar1 bar1 = {var1: "Ballerina", var2: 500}; ClosedBar2 bar2 = {var1: "Language", var2: bar1}; - ClosedBar1 | ClosedBar2 | string a1 = bar1; - ClosedBar1 | ClosedBar2 | string a2 = bar2; - ClosedBar1 | ClosedBar2 | string a3 = "bar2"; + ClosedBar1|ClosedBar2|string a1 = bar1; + ClosedBar1|ClosedBar2|string a2 = bar2; + ClosedBar1|ClosedBar2|string a3 = "bar2"; string[] result = [complexMatch(a1), complexMatch(a2), complexMatch(a3)]; return result; } -function complexMatch(ClosedBar1 | ClosedBar2 | string a) returns string { +function complexMatch(ClosedBar1|ClosedBar2|string a) returns string { match a { var {var1, var2: {var1: v1, var2}} => { return "Matched with three vars : " + var1.toString() + ", " + v1.toString() + @@ -172,16 +172,22 @@ function complexMatch(ClosedBar1 | ClosedBar2 | string a) returns string { function testRuntimeCheck() returns string[] { [int, boolean] tuple = [50, true]; - Foo foo1 = {s: "S", i: 23, f: 5.6, "t": tuple}; - Foo foo2 = {s: "S", i: 23, f: 5.6}; - Foo foo3 = {s: "S", i: 23, f: 5.6, "t": 12}; - - string[] values = [matchRuntimeCheck(foo1), matchRuntimeCheck(foo2), matchRuntimeCheck(foo3), - matchRuntimeCheckWithAny(foo1), matchRuntimeCheckWithAny(foo2), matchRuntimeCheckWithAny(foo3)]; + FooSRMP foo1 = {s: "S", i: 23, f: 5.6, "t": tuple}; + FooSRMP foo2 = {s: "S", i: 23, f: 5.6}; + FooSRMP foo3 = {s: "S", i: 23, f: 5.6, "t": 12}; + + string[] values = [ + matchRuntimeCheck(foo1), + matchRuntimeCheck(foo2), + matchRuntimeCheck(foo3), + matchRuntimeCheckWithAny(foo1), + matchRuntimeCheckWithAny(foo2), + matchRuntimeCheckWithAny(foo3) + ]; return values; } -function matchRuntimeCheck(Foo foo) returns string { +function matchRuntimeCheck(FooSRMP foo) returns string { match foo { var {s, i, f, t: [i2, b]} => { return "Matched with five vars : " + s.toString() + ", " + i.toString() + diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/anydata/anydata_test.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/anydata/anydata_test.bal index c135cd5ec49d..085c964a8034 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/anydata/anydata_test.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/anydata/anydata_test.bal @@ -14,6 +14,8 @@ // specific language governing permissions and limitations // under the License. +import ballerina/lang.regexp; + type Foo record { int a = 0; }; @@ -272,7 +274,7 @@ function testAnydataArray() returns anydata[] { } type ValueType int|float|string|boolean|byte|decimal; -type DataType ValueType|table>|json|xml|ClosedFoo|Foo|map|anydata[]|(); +type DataType ValueType|table>|json|xml|regexp:RegExp|ClosedFoo|Foo|map|anydata[]|(); function testUnionAssignment() returns anydata[] { anydata[] rets = []; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_is_expr_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_is_expr_negative.bal new file mode 100644 index 000000000000..ecb769e0f29d --- /dev/null +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_is_expr_negative.bal @@ -0,0 +1,47 @@ + // Copyright (c) 2024 WSO2 LLC. (http://www.wso2.com). + // + // WSO2 LLC. licenses this file to you under the Apache License, + // Version 2.0 (the "License"); you may not use this file except + // in compliance with the License. + // You may obtain a copy of the License at + // + // http://www.apache.org/licenses/LICENSE-2.0 + // + // Unless required by applicable law or agreed to in writing, + // software distributed under the License is distributed on an + // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + // KIND, either express or implied. See the License for the + // specific language governing permissions and limitations + // under the License. + +function testNeverRuntime10() { + int x = 100; + boolean _ = x is never; +} + +type Record record {| + int i; + never[] j; +|}; + +function testNeverRuntime11() { + Record x = {i: 1, j: []}; + boolean _ = x is never; +} + +function testNeverFieldTypeCheck() { + record {int x;} r2 = {x: 2, "color": "blue"}; + boolean _ = r2 is record {never x?;}; + + record {never? x;} r3 = {x: (), "color": "blue"}; + boolean _ = r3 is record {never x?;}; + + record {int? x;} r4 = {x: 2, "color": "blue"}; + boolean _ = r4 is record {never x?;}; + + record {int x;} & readonly v3 = {x: 2, "color": "blue"}; + boolean _ = v3 is record {never x?;}; + + record {never? x;} & readonly v4 = {x: (), "color": "blue"}; + boolean _ = v4 is record {never x?;}; +} diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_runtime.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_runtime.bal index 645ac4b21f79..2d6620d7f835 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_runtime.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/never/never_type_runtime.bal @@ -72,12 +72,6 @@ function testNeverRuntime9() { assertEquality(true, b); } -function testNeverRuntime10() { - int x = 100; - boolean b = x is never; - assertEquality(false, b); -} - type Record record {| int i; never[] j; @@ -88,12 +82,6 @@ type Record2 record {| never[] j = []; |}; -function testNeverRuntime11() { - Record x = { i: 1, j: [] }; - boolean b = x is never; - assertEquality(false, b); -} - function testNeverRuntime12() { Record x = {i: 1, j: []}; boolean b = x is Record2; @@ -141,15 +129,6 @@ function testNeverFieldTypeCheck() { record {} r1 = {"x": 2, "color": "blue"}; assertEquality(false, r1 is record {never x?;}); - record {int x;} r2 = {x: 2, "color": "blue"}; - assertEquality(false, r2 is record {never x?;}); - - record {never? x;} r3 = {x: (), "color": "blue"}; - assertEquality(false, r3 is record {never x?;}); - - record {int? x;} r4 = {x: 2, "color": "blue"}; - assertEquality(false, r4 is record {never x?;}); - record {} r5 = {}; assertEquality(false, r5 is record {never x?;}); @@ -207,12 +186,6 @@ function testNeverFieldTypeCheck() { record {} & readonly v2 = {"x": 2}; assertEquality(false, v2 is record {never x?;}); - record {int x;} & readonly v3 = {x: 2, "color": "blue"}; - assertEquality(false, v3 is record {never x?;}); - - record {never? x;} & readonly v4 = {x: (), "color": "blue"}; - assertEquality(false, v4 is record {never x?;}); - record {never? x;} & readonly v5 = {x: (), "color": "blue"}; assertEquality(true, v5 is record {never? x;}); diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type_negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type_negative.bal index 9603606618b0..de9645cfba02 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type_negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_inherently_immutable_type_negative.bal @@ -19,11 +19,6 @@ any x = y; } - function cannotDeepEqualReadonly() returns boolean { - readonly arr = [1, 2 , 3]; - return arr == [1, 2 , 3]; - } - function testReadOnlyAssignabilityToUnions() { readonly readonlyVal = 1; error? errOrNil = readonlyVal; diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal index 89489482b63d..459d0ee84059 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/types/readonly/test_selectively_immutable_type.bal @@ -64,12 +64,12 @@ function testSimpleInitializationForSelectivelyImmutableXmlTypes() { assertEquality(c, r4); } -type Employee record {| - Details details; +type EmployeeFoo record {| + DetailsBar details; string department; |}; -type Details record {| +type DetailsBar record {| string name; int id; |}; @@ -78,9 +78,9 @@ function testSimpleInitializationForSelectivelyImmutableListTypes() { int[] & readonly a = [1, 2]; readonly r1 = a; assertTrue(r1 is int[] & readonly); - assertEquality( [1, 2], r1); + assertEquality([1, 2], r1); - Employee & readonly emp = { + EmployeeFoo & readonly emp = { details: { name: "Emma", id: 1234 @@ -88,60 +88,60 @@ function testSimpleInitializationForSelectivelyImmutableListTypes() { department: "finance" }; - [Employee, Employee] & readonly b = [emp, {details: {name: "Jo", id: 5678}, department: "IT"}]; + [EmployeeFoo, EmployeeFoo] & readonly b = [emp, {details: {name: "Jo", id: 5678}, department: "IT"}]; readonly r2 = b; - assertTrue(r2 is [Employee, Employee] & readonly); - assertTrue(r2 is Employee[2] & readonly); + assertTrue(r2 is [EmployeeFoo, EmployeeFoo] & readonly); + assertTrue(r2 is EmployeeFoo[2] & readonly); - [Employee, Employee] & readonly empTup = <[Employee, Employee] & readonly> checkpanic r2; + [EmployeeFoo, EmployeeFoo] & readonly empTup = <[EmployeeFoo, EmployeeFoo] & readonly>checkpanic r2; assertEquality(emp, empTup[0]); record {} rec = empTup[0]; - assertTrue(rec is Employee & readonly); + assertTrue(rec is EmployeeFoo & readonly); assertTrue(rec.isReadOnly()); rec = empTup[1]; - assertTrue(rec is Employee & readonly); + assertTrue(rec is EmployeeFoo & readonly); assertTrue(rec.isReadOnly()); assertEquality("IT", rec["department"]); - assertTrue(rec["details"] is Details & readonly); + assertTrue(rec["details"] is DetailsBar & readonly); assertTrue(rec["details"].isReadOnly()); - Details & readonly details = { + DetailsBar & readonly details = { name: "Jo", id: 9876 }; - [Details[], Employee...] & readonly detEmpTup = [ - [{name: "May", id: 1234}, details], - {details, department: "finance"} - ]; + [DetailsBar[], EmployeeFoo...] & readonly detEmpTup = [ + [{name: "May", id: 1234}, details], + {details, department: "finance"} + ]; readonly r3 = detEmpTup; - assertTrue(r3 is [Details[], Employee...] & readonly); - assertTrue(r3 is [[Details, Details], Employee] & readonly); + assertTrue(r3 is [DetailsBar[], EmployeeFoo...] & readonly); + assertTrue(r3 is [[DetailsBar, DetailsBar], EmployeeFoo] & readonly); - [[Details, Details], Employee] & readonly vals = <[[Details, Details], Employee] & readonly> checkpanic r3; + [[DetailsBar, DetailsBar], EmployeeFoo] & readonly vals = <[[DetailsBar, DetailsBar], EmployeeFoo] & readonly>checkpanic r3; assertTrue(vals[0].isReadOnly()); - Details d1 = vals[0][0]; - assertEquality(
{name: "May", id: 1234}, d1); + DetailsBar d1 = vals[0][0]; + assertEquality({name: "May", id: 1234}, d1); assertTrue(d1.isReadOnly()); - Details d2 = vals[0][1]; + DetailsBar d2 = vals[0][1]; assertEquality(details, d2); assertTrue(d2.isReadOnly()); - Employee e = vals[1]; - assertEquality( {details, department: "finance"}, e); + EmployeeFoo e = vals[1]; + assertEquality({details, department: "finance"}, e); assertTrue(e.isReadOnly()); assertTrue(e.details.isReadOnly()); (int[] & readonly)|string[] arr = [1, 2]; - assertEquality( [1, 2], arr); + assertEquality([1, 2], arr); assertTrue(arr is int[] & readonly); assertTrue(arr.isReadOnly()); arr = ["hello"]; - assertEquality( ["hello"], arr); + assertEquality(["hello"], arr); assertTrue(arr is string[]); assertFalse(arr is string[] & readonly); assertFalse(arr.isReadOnly()); @@ -157,9 +157,9 @@ function testSimpleInitializationForSelectivelyImmutableMappingTypes() { }; readonly r1 = a; assertTrue(r1 is map & readonly); - assertEquality(> {a: true, bool: false, c: false}, r1); + assertEquality(>{a: true, bool: false, c: false}, r1); - Employee & readonly emp = { + EmployeeFoo & readonly emp = { details: { name: "Emma", id: 1234 @@ -168,20 +168,20 @@ function testSimpleInitializationForSelectivelyImmutableMappingTypes() { }; readonly r2 = emp; - assertTrue(r2 is Employee & readonly); + assertTrue(r2 is EmployeeFoo & readonly); assertEquality(emp, r2); any|error val = r2; assertFalse(val is error); - Employee rec = checkpanic val; - assertTrue(rec is Employee & readonly); + EmployeeFoo rec = checkpanic val; + assertTrue(rec is EmployeeFoo & readonly); assertTrue(rec.isReadOnly()); - Details det = rec.details; - assertTrue(det is Details & readonly); + DetailsBar det = rec.details; + assertTrue(det is DetailsBar & readonly); assertTrue(det.isReadOnly()); - Student & readonly st = { + StudentFoo & readonly st = { details: { name: "Jo", id: 4567 @@ -190,25 +190,25 @@ function testSimpleInitializationForSelectivelyImmutableMappingTypes() { "science": ["P", 65] }; readonly r3 = st; - assertTrue(r3 is Student & readonly); + assertTrue(r3 is StudentFoo & readonly); val = r3; assertFalse(val is error); - Student stVal = checkpanic val; + StudentFoo stVal = checkpanic val; assertTrue(stVal.isReadOnly()); assertTrue(stVal.details.isReadOnly()); - assertEquality(
{name: "Jo", id: 4567}, stVal.details); + assertEquality({name: "Jo", id: 4567}, stVal.details); - assertTrue(stVal["math"] is [RESULT, int] & readonly); + assertTrue(stVal["math"] is [RESULTFoo, int] & readonly); assertTrue(stVal["math"].isReadOnly()); - assertEquality(<[RESULT, int]> ["P", 75], stVal["math"]); + assertEquality(<[RESULTFoo, int]>["P", 75], stVal["math"]); - assertTrue(stVal["science"] is [RESULT, int] & readonly); + assertTrue(stVal["science"] is [RESULTFoo, int] & readonly); assertTrue(stVal["science"].isReadOnly()); - assertEquality(<[RESULT, int]> ["P", 65], stVal["science"]); + assertEquality(<[RESULTFoo, int]>["P", 65], stVal["science"]); } -type Identifier record {| +type IdentifierFoo record {| readonly string name; int id; |}; @@ -222,41 +222,41 @@ function testSimpleInitializationForSelectivelyImmutableTableTypes() { readonly r1 = a; assertTrue(r1 is table> & readonly); - table> tbString = >> checkpanic r1; + table> tbString = >>checkpanic r1; assertEquality(2, tbString.length()); map[] mapArr = tbString.toArray(); - assertTrue( mapArr[0] is map & readonly); - assertEquality(> {x: "x", y: "y"}, mapArr[0]); - assertTrue( mapArr[1] is map & readonly); - assertEquality(> {z: "z"}, mapArr[1]); + assertTrue(mapArr[0] is map & readonly); + assertEquality(>{x: "x", y: "y"}, mapArr[0]); + assertTrue(mapArr[1] is map & readonly); + assertEquality(>{z: "z"}, mapArr[1]); - table key(name) & readonly b = table [ + table key(name) & readonly b = table [ {name: "Jo", id: 4567}, {name: "Emma", id: 1234}, {name: "Amy", id: 678} ]; readonly r2 = b; - assertTrue(r2 is table key(name) & readonly); - assertTrue(r2 is table & readonly); + assertTrue(r2 is table key(name) & readonly); + assertTrue(r2 is table & readonly); - table tbDetails = > checkpanic r2; + table tbDetails = >checkpanic r2; assertEquality(3, tbDetails.length()); - Identifier[] detailsArr = tbDetails.toArray(); - assertTrue(detailsArr[0] is Identifier & readonly); - assertEquality( {name: "Jo", id: 4567}, detailsArr[0]); - assertTrue(detailsArr[1] is Identifier & readonly); - assertEquality( {name: "Emma", id: 1234}, detailsArr[1]); - assertTrue(detailsArr[2] is Identifier & readonly); - assertEquality( {name: "Amy", id: 678}, detailsArr[2]); + IdentifierFoo[] detailsArr = tbDetails.toArray(); + assertTrue(detailsArr[0] is IdentifierFoo & readonly); + assertEquality({name: "Jo", id: 4567}, detailsArr[0]); + assertTrue(detailsArr[1] is IdentifierFoo & readonly); + assertEquality({name: "Emma", id: 1234}, detailsArr[1]); + assertTrue(detailsArr[2] is IdentifierFoo & readonly); + assertEquality({name: "Amy", id: 678}, detailsArr[2]); } -type RESULT "P"|"F"; +type RESULTFoo "P"|"F"; -type Student record {| - Details details; - [RESULT, int]...; +type StudentFoo record {| + DetailsBar details; + [RESULTFoo, int]...; |}; type recursiveTuple [int, string|xml, recursiveTuple...]; @@ -296,7 +296,7 @@ function testRuntimeIsTypeForSelectivelyImmutableBasicTypes() { assertFalse(k is readonly); assertTrue(l is readonly); - Employee m = { + EmployeeFoo m = { details: { name: "Maryam", id: 9876 @@ -317,7 +317,7 @@ function testRuntimeIsTypeNegativeForSelectivelyImmutableTypes() { assertFalse(an1 is readonly); assertFalse(a1.isReadOnly()); - Employee emp = { + EmployeeFoo emp = { details: { name: "Emma", id: 1234 @@ -325,54 +325,54 @@ function testRuntimeIsTypeNegativeForSelectivelyImmutableTypes() { department: "finance" }; - [Employee, Employee] b = [emp, {details: {name: "Jo", id: 5678}, department: "IT"}]; + [EmployeeFoo, EmployeeFoo] b = [emp, {details: {name: "Jo", id: 5678}, department: "IT"}]; anydata a2 = b; any an2 = b; - assertFalse(an2 is [Employee, Employee] & readonly); + assertFalse(an2 is [EmployeeFoo, EmployeeFoo] & readonly); assertFalse(an2 is readonly); assertFalse(a2.isReadOnly()); - [Employee, Employee] empTup = <[Employee, Employee]> a2; + [EmployeeFoo, EmployeeFoo] empTup = <[EmployeeFoo, EmployeeFoo]>a2; assertEquality(emp, empTup[0]); record {} rec = empTup[0]; - assertTrue(rec is Employee); - assertFalse(rec is Employee & readonly); + assertTrue(rec is EmployeeFoo); + assertFalse(rec is EmployeeFoo & readonly); assertFalse(rec.isReadOnly()); rec = empTup[1]; - assertTrue(rec is Employee); - assertFalse(rec is Employee & readonly); + assertTrue(rec is EmployeeFoo); + assertFalse(rec is EmployeeFoo & readonly); assertFalse(rec.isReadOnly()); - assertTrue(rec["details"] is Details); - assertFalse(rec["details"] is Details & readonly); + assertTrue(rec["details"] is DetailsBar); + assertFalse(rec["details"] is DetailsBar & readonly); assertFalse(rec["details"].isReadOnly()); - Details & readonly details = { + DetailsBar & readonly details = { name: "Jo", id: 9876 }; - [Details[], Employee...] detEmpTup = [ - [{name: "May", id: 1234}, details], - {details, department: "finance"} - ]; + [DetailsBar[], EmployeeFoo...] detEmpTup = [ + [{name: "May", id: 1234}, details], + {details, department: "finance"} + ]; anydata a3 = detEmpTup; - assertTrue(a3 is [Details[], Employee...]); - assertFalse(a3 is [Details[], Employee...] & readonly); - assertFalse(a3 is [[Details, Details], Employee] & readonly); + assertTrue(a3 is [DetailsBar[], EmployeeFoo...]); + assertFalse(a3 is [DetailsBar[], EmployeeFoo...] & readonly); + assertFalse(a3 is [[DetailsBar, DetailsBar], EmployeeFoo] & readonly); - [Details[], Employee...] vals = <[Details[], Employee...]> a3; + [DetailsBar[], EmployeeFoo...] vals = <[DetailsBar[], EmployeeFoo...]>a3; assertFalse(vals[0].isReadOnly()); - Details d1 = vals[0][0]; + DetailsBar d1 = vals[0][0]; assertFalse(d1.isReadOnly()); - Details d2 = vals[0][1]; + DetailsBar d2 = vals[0][1]; assertEquality(details, d2); assertTrue(d2.isReadOnly()); - Employee e = vals[1]; - assertEquality( {details, department: "finance"}, e); + EmployeeFoo e = vals[1]; + assertEquality({details, department: "finance"}, e); assertFalse(e.isReadOnly()); assertTrue(e.details.isReadOnly()); @@ -396,8 +396,8 @@ function testRuntimeIsTypeNegativeForSelectivelyImmutableTypes() { assertFalse(an5 is readonly); assertFalse(a5.isReadOnly()); - json[] jsonVal = an5; - map a8 = > jsonVal[1]; + json[] jsonVal = an5; + map a8 = >jsonVal[1]; any an8 = a8; assertFalse(a8 is map & readonly); assertFalse(an8 is readonly); @@ -421,15 +421,15 @@ function testRuntimeIsTypeNegativeForSelectivelyImmutableTypes() { assertFalse(an7 is readonly); assertFalse(a7.isReadOnly()); - table key(name) j = table [ + table key(name) j = table [ {name: "Jo", id: 4567}, {name: "Emma", id: 1234}, {name: "Amy", id: 678} ]; anydata a9 = j; any an9 = j; - assertTrue(an9 is table); - assertFalse(an9 is table & readonly); + assertTrue(an9 is table); + assertFalse(an9 is table & readonly); assertFalse(an9 is readonly); assertFalse(a9.isReadOnly());