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/org/wso2/ballerinalang/compiler/BIRPackageSymbolEnter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/BIRPackageSymbolEnter.java index a79e6be0a4af..10a130533543 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 @@ -119,8 +119,6 @@ import org.wso2.ballerinalang.compiler.semantics.model.types.BXMLType; 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.util.BArrayState; import org.wso2.ballerinalang.compiler.util.CompilerContext; import org.wso2.ballerinalang.compiler.util.ImmutableTypeCloner; @@ -156,7 +154,6 @@ 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; @@ -1359,7 +1356,7 @@ 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(symTable.typeEnv(), null, symTable.typeDesc.tsymbol); typedescType.constraint = readTypeFromCp(); @@ -1529,7 +1526,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; } @@ -1732,7 +1729,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; @@ -2156,43 +2153,8 @@ private XmlSubtype readXmlSubtype() throws IOException { // --------------------------------------- End of SemType ----------------------------------------------- } - private BType getType(BType readShape, SymbolEnv pkgEnv, Name name) { - BType type = symbolResolver.lookupSymbolInMainSpace(pkgEnv, name).type; - - if (type != symTable.noType && (!name.value.contains(ANON_PREFIX) || types.isSameBIRShape(readShape, type))) { - return type; - } - - if (pkgEnv.node != null) { - for (BLangTypeDefinition typeDefinition : ((BLangPackage) pkgEnv.node).typeDefinitions) { - BSymbol symbol = typeDefinition.symbol; - - String typeDefName = typeDefinition.name.value; - if (typeDefName.contains(ANON_PREFIX)) { - BType anonType = symbol.type; - - if (types.isSameBIRShape(readShape, anonType)) { - return anonType; - } - } else if (typeDefName.equals(name.value)) { - return symbol.type; - } - } - } 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; - - if (types.isSameBIRShape(readShape, anonType)) { - return anonType; - } - } - } - } - - return type; + private BType lookupSymbolInMainSpace(SymbolEnv pkgEnv, Name name) { + return symbolResolver.lookupSymbolInMainSpace(pkgEnv, name).type; } private byte[] readDocBytes(DataInputStream inputStream) throws IOException { 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 1fa4d0f83db3..c12f323d9c0e 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; @@ -2801,10 +2803,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; @@ -2833,10 +2832,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/JvmObservabilityGen.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/JvmObservabilityGen.java index 92250c5669f9..041f890b2877 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; @@ -985,20 +985,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 07564b70204c..d31aeb5db6c4 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 @@ -513,7 +513,7 @@ private void linkTypeDefinitions(BIRPackage module, boolean isEntry) { private void linkModuleFunction(PackageID packageID, String initClass, String funcName) { BInvokableType funcType = - new BInvokableType(symbolTable.typeEnv(), Collections.emptyList(), null, BType.createNilType(), null); + new BInvokableType(symbolTable.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, 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 27938d6c9f82..39db84875ce4 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 @@ -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.PackageID; import org.objectweb.asm.Handle; @@ -43,6 +44,7 @@ import org.wso2.ballerinalang.compiler.bir.model.BIRTerminator; import org.wso2.ballerinalang.compiler.bir.model.VarKind; import org.wso2.ballerinalang.compiler.bir.model.VarScope; +import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.model.SymbolTable; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableSymbol; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BInvokableTypeSymbol; @@ -404,15 +406,7 @@ private void handleErrorRetInUnion(int returnVarRefIndex, Listx 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/interop/JMethodRequest.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/bir/codegen/interop/JMethodRequest.java index dfa5278a93e8..fedbe0f81b44 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,14 @@ */ package org.wso2.ballerinalang.compiler.bir.codegen.interop; +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; @@ -96,16 +96,7 @@ static JMethodRequest build(InteropValidationRequest.MethodValidationRequest met BType returnType = unifier.build(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 a1c315564530..76968ea5d72a 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 @@ -387,19 +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(symbolTable.typeEnv(), 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); } } 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 912534017e7a..9200ac1e3858 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 @@ -180,7 +180,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; } 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 fb237e701230..b70b34854b33 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 @@ -6695,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); @@ -9344,7 +9344,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; } 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 59ed2363b1ed..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; @@ -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; 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 72c44d404779..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 @@ -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/semantics/analyzer/CodeAnalyzer.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/CodeAnalyzer.java index 9aad0d7e3b88..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 @@ -20,6 +20,8 @@ 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; @@ -1397,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()); } } @@ -1529,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); } } @@ -2274,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 @@ -3335,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); @@ -3541,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); } } @@ -3570,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 */ 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 8c2f71aac930..274a43021631 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 @@ -620,7 +620,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); } } @@ -948,22 +948,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); @@ -1083,7 +1067,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); } } 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/QueryTypeChecker.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/QueryTypeChecker.java index c13545e3b16a..7e2177336ed9 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; @@ -671,12 +672,12 @@ 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; @@ -914,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 (exprType.tag != TypeTags.SEMANTIC_ERROR && !types.isOrderedType(exprType, false)) { + if (exprType.tag != TypeTags.SEMANTIC_ERROR && !types.isOrderedType(exprType)) { dlog.error(((BLangOrderKey) orderKeyNode).expression.pos, DiagnosticErrorCode.ORDER_BY_NOT_SUPPORTED); } } 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 index bee32420fa7b..c1339a43921e 100644 --- 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 @@ -32,13 +32,9 @@ 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.BObjectType; 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.semantics.model.types.SemNamedType; import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral; -import org.wso2.ballerinalang.compiler.util.TypeTags; import java.math.BigDecimal; import java.util.LinkedHashSet; @@ -105,68 +101,24 @@ public static SemType resolveSingletonType(Object value, TypeKind targetTypeKind } } - public static SemType semType(BType t) { - return semType(t, false); - } - public static boolean isSubtypeSimple(BType bt, BasicTypeBitSet bbs) { - SemType t = SemTypeHelper.semType(bt); - return SemTypes.isSubtypeSimple(t, bbs); + return SemTypes.isSubtypeSimple(bt.semType(), bbs); } - public static boolean isSubtype(Context context, BType bt, SemType st) { - SemType s = SemTypeHelper.semType(bt); - return SemTypes.isSubtype(context, s, st); + public static boolean isSubtypeSimpleNotNever(BType bt, BasicTypeBitSet bbs) { + return SemTypes.isSubtypeSimpleNotNever(bt.semType(), bbs); } - public static SemType semType(BType t, boolean ignoreObjectTypeIds) { - if (t == null) { // TODO: may be able to fix after tackling bir recursion issue - return PredefinedType.NEVER; - } + public static boolean containsBasicType(BType bt, BasicTypeBitSet bbs) { + return SemTypes.containsBasicType(bt.semType(), bbs); + } - if (t.tag == TypeTags.TYPEREFDESC) { - return semType(((BTypeReferenceType) t).referredType, ignoreObjectTypeIds); - } + public static boolean containsType(Context ctx, BType bt, SemType bbs) { + return SemTypes.containsType(ctx, bt.semType(), bbs); + } - switch (t.tag) { - case TypeTags.INTERSECTION: - case TypeTags.ANYDATA: - case TypeTags.JSON: - case TypeTags.ANY: - case TypeTags.READONLY: - case TypeTags.ARRAY: - case TypeTags.TUPLE: - case TypeTags.MAP: - case TypeTags.RECORD: - case TypeTags.INVOKABLE: - case TypeTags.FUTURE: - case TypeTags.TYPEDESC: - case TypeTags.STREAM: - case TypeTags.ERROR: - case TypeTags.TABLE: - case TypeTags.SEQUENCE: - case TypeTags.PARAMETERIZED_TYPE: - case TypeTags.NONE: - return t.semType(); - case TypeTags.NULL_SET: - case TypeTags.SEMANTIC_ERROR: - return PredefinedType.NEVER; - case TypeTags.UNION: - if (ignoreObjectTypeIds) { - return ((BUnionType) t).semTypeIgnoringTypeIds(); - } - return t.semType(); - case TypeTags.OBJECT: - if (ignoreObjectTypeIds) { - return ((BObjectType) t).semTypeIgnoringTypeIds(); - } - return t.semType(); - default: - if (isFullSemType(t.tag)) { - return t.semType(); - } - return PredefinedType.NEVER; - } + public static boolean isSubtype(Context context, BType bt, SemType st) { + return SemTypes.isSubtype(context, bt.semType(), st); } public static boolean isSimpleOrString(TypeKind kind) { @@ -185,36 +137,6 @@ public static boolean isSimpleOrString(TypeKind kind) { } } - public static boolean isFullSemType(int tag) { - switch (tag) { - case TypeTags.NIL: - case TypeTags.BOOLEAN: - case TypeTags.INT: - case TypeTags.BYTE: - 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.FLOAT: - case TypeTags.DECIMAL: - case TypeTags.STRING: - case TypeTags.CHAR_STRING: - case TypeTags.FINITE: - case TypeTags.XML: - case TypeTags.XML_ELEMENT: - case TypeTags.XML_COMMENT: - case TypeTags.XML_PI: - case TypeTags.XML_TEXT: - case TypeTags.HANDLE: - case TypeTags.REGEXP: - return true; - default: - return false; - } - } - /** * Returns the basic type of singleton. *

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 5d1efc02a5ac..d539cfbb1619 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,7 @@ import io.ballerina.compiler.api.symbols.DiagnosticState; import io.ballerina.projects.ModuleDescriptor; import io.ballerina.tools.diagnostics.Location; +import io.ballerina.types.PredefinedType; import org.ballerinalang.compiler.CompilerPhase; import org.ballerinalang.model.TreeBuilder; import org.ballerinalang.model.elements.AttachPoint; @@ -1246,7 +1247,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)); } @@ -1336,7 +1337,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: @@ -1413,21 +1414,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) { @@ -2066,7 +2055,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; @@ -3983,7 +3972,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; @@ -4023,8 +4012,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) { @@ -4932,7 +4919,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); } diff --git a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolEnter.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolEnter.java index 082b5d7b64b8..5096d4339127 100644 --- a/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolEnter.java +++ b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/analyzer/SymbolEnter.java @@ -37,7 +37,6 @@ import org.ballerinalang.model.tree.OrderedNode; import org.ballerinalang.model.tree.TopLevelNode; import org.ballerinalang.model.tree.TypeDefinition; -import org.ballerinalang.model.tree.statements.StatementNode; import org.ballerinalang.model.tree.types.TypeNode; import org.ballerinalang.model.types.TypeKind; import org.ballerinalang.util.diagnostic.DiagnosticErrorCode; @@ -131,7 +130,6 @@ import org.wso2.ballerinalang.compiler.tree.expressions.BLangUnaryExpr; import org.wso2.ballerinalang.compiler.tree.expressions.BLangXMLAttribute; import org.wso2.ballerinalang.compiler.tree.expressions.BLangXMLQName; -import org.wso2.ballerinalang.compiler.tree.statements.BLangAssignment; import org.wso2.ballerinalang.compiler.tree.statements.BLangXMLNSStatement; import org.wso2.ballerinalang.compiler.tree.types.BLangArrayType; import org.wso2.ballerinalang.compiler.tree.types.BLangBuiltInRefTypeNode; @@ -160,7 +158,6 @@ import org.wso2.ballerinalang.compiler.util.TypeTags; import org.wso2.ballerinalang.util.Flags; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -237,7 +234,6 @@ public class SymbolEnter extends BLangNodeVisitor { private List unresolvedTypes; private Set unresolvedRecordDueToFields; private boolean resolveRecordsUnresolvedDueToFields; - private List unresolvedClasses; private final HashSet unknownTypeRefs; private final List importedPackages; private int typePrecedence; @@ -246,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; @@ -497,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; @@ -519,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; @@ -534,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) { @@ -1239,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)) { @@ -3055,7 +2940,7 @@ BType getRestParamType(BRecordType recordType) { } } - SemType s = SemTypeHelper.semType(recordType); + SemType s = recordType.semType(); SemType anydata = types.anydata(); if (SemTypes.containsBasicType(s, PredefinedType.ERROR)) { if (types.isSubtype(s, Core.union(anydata, PredefinedType.ERROR))) { @@ -3674,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; } /** @@ -3820,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; @@ -4660,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); @@ -4910,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; @@ -4979,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); 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 98b4d8baaf94..bb7f3fd18b54 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; @@ -1574,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; @@ -1878,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; @@ -2101,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; @@ -2195,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) { 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 5c5e751d6c92..a47b1184e68e 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 @@ -216,7 +216,6 @@ 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; @@ -1236,7 +1235,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); } } @@ -1823,7 +1822,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); } } @@ -2566,7 +2565,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); } } @@ -3658,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; } @@ -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; @@ -5459,17 +5438,12 @@ private BType getXmlStringBinaryOpResultType(BType opType, BType constituentType } 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) { @@ -5845,7 +5819,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) { @@ -6367,16 +6341,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) { @@ -6581,7 +6546,7 @@ 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; } @@ -6598,11 +6563,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) { @@ -7666,7 +7632,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); @@ -7706,7 +7672,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); } } @@ -8687,7 +8653,7 @@ 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<>(); @@ -8770,24 +8736,9 @@ private BType checkOptionalFieldAccessExpr(BLangFieldBasedAccess fieldAccessExpr } 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) { @@ -8812,7 +8763,7 @@ private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, A varRefType = nilRemovedSet.size() == 1 ? nilRemovedSet.iterator().next() : BUnionType.create(symTable.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, @@ -8837,7 +8788,7 @@ private BType checkIndexAccessExpr(BLangIndexBasedAccess indexBasedAccessExpr, A if (varRefType == symTable.semanticError) { indexBasedAccessExpr.indexExpr.setBType(symTable.semanticError); return symTable.semanticError; - } else if (types.isSubTypeOfMapping(varRefType)) { + } else if (types.isSubTypeOfMapping(varRefType.semType())) { checkExpr(indexExpr, symTable.stringType, data); if (indexExpr.getBType() == symTable.semanticError) { @@ -9499,7 +9450,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 { @@ -9516,7 +9467,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); } } @@ -9533,7 +9484,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); } } @@ -9640,7 +9591,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); } @@ -9649,23 +9600,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) { 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 b25e30ac1715..2612260cb3e6 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; @@ -769,7 +770,7 @@ 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()) { 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 1071d73ae33e..2e61b0993502 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,15 +20,25 @@ import io.ballerina.runtime.api.flags.SymbolFlags; import io.ballerina.tools.diagnostics.DiagnosticCode; import io.ballerina.tools.diagnostics.Location; +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 org.ballerinalang.model.Name; +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; @@ -49,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; @@ -60,17 +67,14 @@ 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.BStreamType; @@ -80,8 +84,6 @@ 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; @@ -110,7 +112,6 @@ 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.Names; import org.wso2.ballerinalang.compiler.util.NumericLiteralSupport; @@ -140,6 +141,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; @@ -153,11 +159,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. @@ -177,7 +181,6 @@ public class Types { private int finiteTypeCount = 0; private final BLangAnonymousModelHelper anonymousModelHelper; private SymbolEnv env; - private boolean ignoreObjectTypeIds = false; protected final Context semTypeCtx; private static final String BASE_16 = "base16"; @@ -261,6 +264,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) { @@ -323,101 +330,100 @@ public boolean isLaxFieldAccessAllowed(BType type) { if (type.tag == TypeTags.SEMANTIC_ERROR) { return false; } + return isLaxFieldAccessAllowed(type.semType()); + } - type = Types.getImpliedType(type); - Set visited = new HashSet<>(); - return isLaxType(type, visited) == 1 || isAssignable(type, symTable.xmlType); + public boolean isLaxFieldAccessAllowed(SemType t) { + if (Core.isNever(t)) { + return false; + } + return isSubtypeSimple(t, PredefinedType.XML) || isLaxType(t); } - // TODO : clean - public int isLaxType(BType type, Set visited) { - type = getImpliedType(type); - if (!visited.add(type)) { - return -1; + /** + * 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: - 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; + + Optional> optMatList = Core.mappingAtomicTypesInUnion(semTypeCtx, t); + if (optMatList.isEmpty()) { + return false; } - return 0; - } - public boolean isSameType(BType source, BType target) { - return isSameType(source, target, new HashSet<>()); + List matList = optMatList.get(); + return matList.stream().allMatch(mat -> mat.names().length == 0 && isLaxType(Core.cellInnerVal(mat.rest()))); } - public boolean isSameOrderedType(BType source, BType target) { - return isSameOrderedType(source, target, new HashSet<>()); - } + boolean isUniqueType(Iterable typeList, BType type) { + type = Types.getImpliedType(type); + boolean isRecord = type.tag == TypeTags.RECORD; - 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; + 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 SemType anydata() { - return Core.createAnydata(semTypeCtx); + public boolean isSameType(BType source, BType target) { + return isSameType(source.semType(), target.semType()); } - public boolean isAnydata(BType type) { - return SemTypeHelper.isSubtype(semTypeCtx, type, Core.createAnydata(semTypeCtx)); - } + 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) { // TODO: remove + public boolean isValueType(BType type) { return switch (getImpliedType(type).tag) { case TypeTags.BOOLEAN, TypeTags.BYTE, @@ -441,49 +447,16 @@ boolean isBasicNumericType(BType bType) { return type.tag < TypeTags.STRING || TypeTags.isIntegerTypeTag(type.tag); } - boolean finiteTypeContainsNumericTypeValues(BFiniteType finiteType) { - return !Core.isEmpty(semTypeCtx, SemTypes.intersect(finiteType.semType(), PredefinedType.NUMBER)); - } - 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, @@ -714,31 +687,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) { @@ -760,14 +713,18 @@ BType mergeTypes(BType typeFirst, BType 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); @@ -806,38 +763,66 @@ 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); + } + + 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); } - private boolean isAssignable(BType source, BType target, Set unresolvedTypes) { - SemType semSource = SemTypeHelper.semType(source, this.ignoreObjectTypeIds); - SemType semTarget = SemTypeHelper.semType(target, this.ignoreObjectTypeIds); - return isSubtype(semSource, semTarget); + + /** + * 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; + } + + 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); } public boolean isSubtype(SemType t1, SemType t2) { return SemTypes.isSubtype(semTypeCtx, t1, t2); } + public boolean isSubtype(BType t1, SemType t2) { + return SemTypeHelper.isSubtype(semTypeCtx, t1, t2); + } + BField getTableConstraintField(BType constraintType, String fieldName) { constraintType = getImpliedType(constraintType); switch (constraintType.tag) { @@ -863,84 +848,6 @@ BField getTableConstraintField(BType constraintType, String fieldName) { return null; } - private boolean hasIncompatibleReadOnlyFlags(long targetFlags, long sourceFlags) { - return Symbols.isFlagOn(targetFlags, Flags.READONLY) && !Symbols.isFlagOn(sourceFlags, Flags.READONLY); - } - - 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 isFunctionTypeAssignable(BInvokableType source, BInvokableType target, - Set unresolvedTypes) { - if (hasIncompatibleIsolatedFlags(source, target) || hasIncompatibleTransactionalFlags(source, target)) { - return false; - } - - if (Symbols.isFlagOn(target.getFlags(), Flags.ANY_FUNCTION)) { - return true; - } - if (Symbols.isFlagOn(source.getFlags(), Flags.ANY_FUNCTION) && - !Symbols.isFlagOn(target.getFlags(), 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)) { @@ -999,7 +906,7 @@ public BLangExpression addConversionExprIfRequired(BLangExpression expr, BType l return addConversionExprIfRequired(expr, Types.getReferredType(lhsType)); } - if (isSameType(rhsType, lhsType)) { + if (rhsType.tag == lhsType.tag && isSameType(rhsType, lhsType)) { return expr; } @@ -1046,13 +953,8 @@ public boolean isSelectivelyImmutableType(BType type, Set unresolvedTypes return isSelectivelyImmutableType(type, unresolvedTypes, false, packageID); } - private boolean isSelectivelyImmutableType(BType type, Set unresolvedTypes, boolean forceCheck, + private boolean isSelectivelyImmutableType(BType input, 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)) { @@ -1189,299 +1091,101 @@ public static boolean containsTypeParams(BInvokableType type) { 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; + 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(typeEnv(), 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(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; + } + // 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; } - if (Symbols.isFlagOn(target.getFlags(), Flags.ANY_FUNCTION) && - Symbols.isFlagOn(source.getFlags(), Flags.ANY_FUNCTION)) { - return true; - } - - if (Symbols.isFlagOn(target.getFlags(), Flags.ANY_FUNCTION) || - Symbols.isFlagOn(source.getFlags(), 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.getFlags(), Flags.ISOLATED) && - !Symbols.isFlagOn(source.getFlags(), Flags.ISOLATED); - } - - private boolean hasIncompatibleTransactionalFlags(BInvokableType source, BInvokableType target) { - return Symbols.isFlagOn(source.getFlags(), Flags.TRANSACTIONAL) && - !Symbols.isFlagOn(target.getFlags(), 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.getSize() == rhsArrayType.getSize(); - } - - 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.getFlags(), Flags.ISOLATED) && - !Symbols.isFlagOn(rhsType.getFlags(), 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(typeEnv(), 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(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; - } - // 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; - } + 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) { @@ -1975,7 +1679,7 @@ && isAllErrorMembers((BUnionType) actualType))) { return false; } - public boolean isTypeCastable(BLangExpression expr, BType source, BType target, SymbolEnv env) { + 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 || @@ -1983,132 +1687,36 @@ public boolean isTypeCastable(BLangExpression expr, BType source, BType target, return true; } + SemType sourceSemType = sourceType.semType(); + SemType targetSemType = targetType.semType(); + // 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) { + if (containsErrorType(sourceSemType) && !containsErrorType(targetSemType)) { 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 && getFiniteTypeForAssignableValues(sourceType, targetType).isPresent()) { - validTypeCast = true; - } - - if (targetType.tag == TypeTags.FINITE && getFiniteTypeForAssignableValues(targetType, sourceType).isPresent()) { - validTypeCast = true; - } - - if (validTypeCast) { - if (isValueType(sourceType)) { - setImplicitCastExpr(expr, sourceType, symTable.anyType); - } + if (isNumericConversionPossible(sourceType, targetType)) { return true; } - return false; + return intersectionExists(sourceSemType, targetSemType); } - 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; - } - } - } - } + public boolean containsErrorType(SemType t) { + return SemTypes.containsBasicType(t, PredefinedType.ERROR); + } - if (!isTargetNumericType && targetType.tag != TypeTags.UNION) { + boolean isNumericConversionPossible(BType sourceType, BType targetType) { + Optional targetNumericType = Core.singleNumericType(targetType.semType()); + if (targetNumericType.isEmpty()) { 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; - } - } - break; - case TypeTags.FINITE: - if (finiteTypeContainsNumericTypeValues((BFiniteType) sourceType)) { - return true; - } - break; - } - return false; + return !Core.isEmpty(semTypeCtx, SemTypes.intersect(sourceType.semType(), PredefinedType.NUMBER)); } public boolean isAllErrorMembers(BUnionType actualType) { - return actualType.getMemberTypes().stream().allMatch(t -> isAssignable(t, symTable.errorType)); + return isSubtype(actualType, PredefinedType.ERROR); } public void setImplicitCastExpr(BLangExpression expr, BType actualType, BType targetType) { @@ -2177,893 +1785,8 @@ public boolean isValidErrorDetailType(BType detailType) { // 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.getFlags(), Flags.READONLY) == - Symbols.isFlagOn(source.getFlags(), 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; - } - } - - 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); - } - - @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; - } - - @Override - public Boolean visit(BIntersectionType tIntersectionType, BType s) { - if (s.tag != TypeTags.INTERSECTION) { - return false; - } - return visit(tIntersectionType.effectiveType, ((BIntersectionType) s).effectiveType); - } - - @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; - } - - 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); - } - - @Override - public Boolean visit(BFiniteType t, BType s) { - return s == t; - } - - @Override - public Boolean visit(BParameterizedType t, BType s) { - if (s.tag != TypeTags.PARAMETERIZED_TYPE) { - return false; - } - - BParameterizedType sType = (BParameterizedType) s; - return sType.paramSymbol.equals(t.paramSymbol) && - equality.test(sType.paramValueType, t.paramValueType, new HashSet<>()); - } - - public Boolean visit(BTypeReferenceType t, BType s) { - BType constraint = s; - if (s.tag == TypeTags.TYPEREFDESC) { - constraint = getImpliedType(((BTypeReferenceType) s).referredType); - } - 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<>()); - } - - 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; - } - - BIRSameShapeVisitor birSameShapeVisitor = new BIRSameShapeVisitor(unresolvedTypes, this::isSameBIRShape); - - if (target.accept(birSameShapeVisitor, source)) { - return true; - } - - unresolvedTypes.remove(pair); - return false; - } - - @Deprecated - private class BIRSameShapeVisitor extends BSameTypeVisitor { - - BIRSameShapeVisitor(Set unresolvedTypes, TypeEqualityPredicate equality) { - super(unresolvedTypes, equality); - } - - @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; - } - - SemType semSource = s.semType(); - SemType semTarget = t.semType(); - return SemTypes.isSameType(semTypeCtx, semSource, semTarget); - } - - @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); - } - } - - 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 checkValueSpaceHasSameOrderedType(((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 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); - } - - @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(); - - if (checkUnionHasAllFiniteOrNilMembers(sourceTypes) && checkUnionHasAllFiniteOrNilMembers(targetTypes)) { - BType type = getImpliedType(target.getMemberTypes().iterator().next()); - BFiniteType finiteType; - if (type.tag == TypeTags.NIL) { - finiteType = BFiniteType.newSingletonBFiniteType(null, PredefinedType.NIL); - } else { - finiteType = (BFiniteType) type; - } - return checkValueSpaceHasSameOrderedType(finiteType, sUnionType.getMemberTypes().iterator().next()); - } - - return checkSameOrderedTypesInUnionMembers(sourceTypes, targetTypes); - } - - private boolean checkSameOrderedTypesInUnionMembers(LinkedHashSet sourceTypes, - LinkedHashSet targetTypes) { - - 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; - } - } - return true; - } - - @Override - public Boolean visit(BFiniteType t, BType s) { - return checkValueSpaceHasSameOrderedType(t, s); - } - - private boolean hasSameReadonlyFlag(BType source, BType target) { - return Symbols.isFlagOn(target.getFlags(), Flags.READONLY) == - Symbols.isFlagOn(source.getFlags(), 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; - } - - @Override - public Boolean visit(BMapType t, BType s) { - return false; - } - - @Override - public Boolean visit(BFutureType t, BType s) { - return false; - } - - @Override - public Boolean visit(BXMLType t, BType s) { - return false; - } - - @Override - public Boolean visit(BJSONType t, BType s) { - return false; - } - - - @Override - public Boolean visit(BObjectType t, BType s) { - return false; - } - - @Override - public Boolean visit(BRecordType t, BType s) { - return false; - } - - @Override - public Boolean visit(BStreamType t, BType s) { - return false; - } - - @Override - public Boolean visit(BTableType t, BType s) { - return false; - } - - @Override - public Boolean visit(BInvokableType t, BType s) { - return false; - } - - @Override - public Boolean visit(BIntersectionType tIntersectionType, BType s) { - return this.visit(getImpliedType(tIntersectionType), s); - } - - @Override - public Boolean visit(BErrorType t, BType s) { - return false; - } - - @Override - public Boolean visit(BTypedescType t, BType s) { - return false; - } - - public Boolean visit(BTypeReferenceType t, BType s) { - return this.visit(getImpliedType(t), t); - } - - @Override - public Boolean visit(BParameterizedType t, BType s) { - return false; - } - } - - 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) { - return SemTypes.isSubtype(semTypeCtx, referredType.semType(), PredefinedType.NIL); - } - return false; - } - - private boolean checkUnionHasSameType(BUnionType unionType, BType baseType) { - LinkedHashSet memberTypes = unionType.getMemberTypes(); - for (BType type : memberTypes) { - type = getImpliedType(type); - if (type.tag == TypeTags.FINITE) { - Set broadTypes = SemTypeHelper.broadTypes((BFiniteType) type, symTable); - for (BType broadType : broadTypes) { - if (!isSameOrderedType(broadType, baseType)) { - return false; - } - } -// } 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; - } - } - } - return true; - } - - /** - * Checks whether all values belong to the same ordered type. - * - * @param finiteType BFiniteType to be checked - * @param type BType to be checked - * @return true if all values of two types belong to the same ordered type, false otherwise - */ - private boolean checkValueSpaceHasSameOrderedType(BFiniteType finiteType, BType type) { - BType baseType = getImpliedType(type); - Set broadTypes = SemTypeHelper.broadTypes(finiteType, symTable); - if (baseType.tag == TypeTags.FINITE) { - BType baseExprType = broadTypes.iterator().next(); - return checkValueSpaceHasSameOrderedType(((BFiniteType) baseType), baseExprType); - } - - for (BType broadType : broadTypes) { - if (!isSameOrderedType(broadType, baseType)) { - return false; - } - } - - return true; - } - - private boolean checkUnionHasAllFiniteOrNilMembers(LinkedHashSet memberTypes) { - for (BType bType : memberTypes) { - BType type = getImpliedType(bType); - if (type.tag != TypeTags.FINITE && !isNil(type)) { - return false; - } - } - return true; - } - - 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; - } - - if (!rhsType.sealed && !isAssignable(rhsType.restFieldType, lhsField.type, unresolvedTypes)) { - return false; - } - - 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; - } - } - 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; - } - - private boolean isInvalidNeverField(BField lhsField, BRecordType rhsType) { - if (getImpliedType(lhsField.type).tag != NEVER || rhsType.sealed) { - return false; - } - - 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; - }; - } - - 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 (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); - - if (!lhsFuncIsResource && !matchingFuncIsResource) { - return matchingFunction; - } - - if (!lhsFuncIsResource || !matchingFuncIsResource) { - return Optional.empty(); - } - - List lhsFuncPathTypes = ((BResourceFunction) lhsFunc).pathSegmentSymbols; - List rhsFuncPathTypes = ((BResourceFunction) matchingFunc).pathSegmentSymbols; - - int lhsFuncResourcePathTypesSize = lhsFuncPathTypes.size(); - if (lhsFuncResourcePathTypesSize != rhsFuncPathTypes.size()) { - return Optional.empty(); - } - - for (int i = 0; i < lhsFuncResourcePathTypesSize; i++) { - if (!isAssignable(lhsFuncPathTypes.get(i).type, rhsFuncPathTypes.get(i).type)) { - return Optional.empty(); - } - } - - return matchingFunction; - } - - 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); - } - - private Set getEffectiveMemberTypes(BUnionType unionType) { - Set memTypes = new LinkedHashSet<>(); + private Set getEffectiveMemberTypes(BUnionType unionType) { + Set memTypes = new LinkedHashSet<>(); for (BType memberType : unionType.getMemberTypes()) { switch (memberType.tag) { @@ -3295,9 +2018,8 @@ private Optional getFiniteTypeForAssignableValues(BType finiteType, BType BFiniteType bFiniteType = (BFiniteType) finiteType; List newValueSpace = new ArrayList<>(bFiniteType.valueSpace.length); - SemType targetSemType = SemTypeHelper.semType(targetType); for (SemNamedType semNamedType : bFiniteType.valueSpace) { - if (SemTypes.isSubtype(semTypeCtx, semNamedType.semType(), targetSemType)) { + if (SemTypes.isSubtype(semTypeCtx, semNamedType.semType(), targetType.semType())) { newValueSpace.add(semNamedType); } } @@ -3356,45 +2078,12 @@ BType getTypeForUnionTypeMembersAssignableToType(BUnionType unionType, BType tar } 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); - } - - 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; + return isAnydata(intersect); } /** @@ -3434,8 +2123,7 @@ boolean isStringSubtype(BType type) { * @return a boolean */ boolean validNumericTypeExists(BType type) { - SemType t = SemTypeHelper.semType(type); - SemType tButNil = Core.diff(t, PredefinedType.NIL); // nil lift + SemType tButNil = Core.diff(type.semType(), PredefinedType.NIL); // nil lift BasicTypeBitSet basicTypeBitSet = Core.widenToBasicTypes(tButNil); return basicTypeBitSet.equals(PredefinedType.INT) || basicTypeBitSet.equals(PredefinedType.FLOAT) || @@ -3443,51 +2131,13 @@ boolean validNumericTypeExists(BType type) { } 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: - return !Core.isEmpty(semTypeCtx, SemTypes.intersect(type.semType(), PredefinedType.INT)); - default: - return false; - } + SemType s = bType.semType(); + s = Core.diff(s, PredefinedType.NIL); // nil lift + return SemTypes.isSubtypeSimpleNotNever(s, PredefinedType.INT); } public boolean isStringSubType(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 (!isStringSubType(memType)) { - return false; - } - } - return true; - case TypeTags.FINITE: - SemType semType = type.semType(); - return SemTypes.isSubtype(semTypeCtx, semType, PredefinedType.STRING); - default: - return false; - } + return SemTypeHelper.isSubtypeSimpleNotNever(type, PredefinedType.STRING); } /** @@ -3523,270 +2173,41 @@ private Set expandAndGetMemberTypesRecursiveHelper(BType bType, if (!visited.add(unionType)) { return memberTypes; } - unionType.getMemberTypes().forEach(member -> - memberTypes.addAll(expandAndGetMemberTypesRecursiveHelper(member, visited)) - ); - break; - case TypeTags.ARRAY: - BType arrayElementType = ((BArrayType) referredType).getElementType(); - - // 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(typeEnv(), arrayElementType)); - } - - if (getImpliedType(arrayElementType).tag == TypeTags.UNION) { - Set elementUnionTypes = expandAndGetMemberTypesRecursiveHelper(arrayElementType, visited); - elementUnionTypes.forEach( - elementUnionType -> memberTypes.add(new BArrayType(typeEnv(), elementUnionType))); - } - memberTypes.add(bType); - break; - case TypeTags.MAP: - BType mapConstraintType = ((BMapType) referredType).getConstraint(); - if (getImpliedType(mapConstraintType).tag == TypeTags.UNION) { - Set constraintUnionTypes = - expandAndGetMemberTypesRecursiveHelper(mapConstraintType, visited); - constraintUnionTypes.forEach(constraintUnionType -> memberTypes.add( - new BMapType(symTable.typeEnv(), TypeTags.MAP, constraintUnionType, - symTable.mapType.tsymbol))); - } - memberTypes.add(bType); - break; - default: - memberTypes.add(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; - } + unionType.getMemberTypes().forEach(member -> + memberTypes.addAll(expandAndGetMemberTypesRecursiveHelper(member, visited)) + ); + break; + case TypeTags.ARRAY: + BType arrayElementType = ((BArrayType) referredType).getElementType(); - if (!equalityIntersectionExists(expandAndGetMemberTypesRecursive(rhsField.type), - expandAndGetMemberTypesRecursive(lhsType.restFieldType))) { - return false; + // 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(typeEnv(), arrayElementType)); } - } - } - - 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; - } - } + if (getImpliedType(arrayElementType).tag == TypeTags.UNION) { + Set elementUnionTypes = expandAndGetMemberTypesRecursiveHelper(arrayElementType, visited); + elementUnionTypes.forEach( + elementUnionType -> memberTypes.add(new BArrayType(typeEnv(), elementUnionType))); + } + memberTypes.add(bType); + break; + case TypeTags.MAP: + BType mapConstraintType = ((BMapType) referredType).getConstraint(); + if (getImpliedType(mapConstraintType).tag == TypeTags.UNION) { + Set constraintUnionTypes = + expandAndGetMemberTypesRecursiveHelper(mapConstraintType, visited); + constraintUnionTypes.forEach(constraintUnionType -> memberTypes.add( + new BMapType(symTable.typeEnv(), TypeTags.MAP, constraintUnionType, + symTable.mapType.tsymbol))); + } + memberTypes.add(bType); + break; + default: + memberTypes.add(bType); } - return false; + return memberTypes; } public BType getRemainingMatchExprType(BType originalType, BType typeToRemove, SymbolEnv env) { @@ -3871,8 +2292,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; } @@ -3917,10 +2338,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.getFlags(), 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) { @@ -4060,7 +2483,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); } } @@ -4077,6 +2500,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<>()); @@ -4674,54 +3101,6 @@ public BErrorType createErrorType(BType detailType, long flags, SymbolEnv env) { 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) { @@ -4732,52 +3111,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; @@ -4837,8 +3170,7 @@ private BType getRemainingType(BUnionType originalType, List removeTypes) private BType getRemainingType(BFiniteType originalType, List removeTypes) { SemType removeSemType = PredefinedType.NEVER; for (BType removeType : removeTypes) { - SemType semTypeToRemove = SemTypeHelper.semType(removeType); - removeSemType = SemTypes.union(removeSemType, semTypeToRemove); + removeSemType = SemTypes.union(removeSemType, removeType.semType()); } List newValueSpace = new ArrayList<>(); @@ -4862,6 +3194,18 @@ private BType getRemainingType(BFiniteType originalType, List removeTypes 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) { BType type = getImpliedType(bType); // Since JSON, ANY and ANYDATA by default contain null, we need to create a new respective type which @@ -5029,52 +3373,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); + BasicTypeBitSet nilOrError = (BasicTypeBitSet) Core.union(PredefinedType.NIL, PredefinedType.ERROR); + return SemTypeHelper.isSubtypeSimpleNotNever(type, nilOrError); } public boolean hasFillerValue(BType type) { @@ -5258,79 +3558,24 @@ private boolean checkFillerValue(BArrayType type) { /** * Check whether a type is an ordered type. * - * @param type type to be checked - * @param hasCycle whether there is a cycle + * @param type type to be checked * @return boolean whether the type is an ordered type or not */ - 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)) { - if (!checkValueSpaceHasSameOrderedType((BFiniteType) memType, firstTypeInUnion)) { - 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: - return isOrderedType(type.semType()); - default: - return isSimpleBasicType(type.tag); - } + 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. *

- * Note: this is kind of similar to comparable() in nBallerina - *

+ * 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 @@ -5344,13 +3589,101 @@ public boolean isOrderedType(SemType t) { 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; + } + } + return true; + } + + return false; + } + + boolean comparable(BType t1, BType t2) { + return comparable(t1.semType(), t2.semType()); + } + + /** + * 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 t1 first semType + * @param t2 second semType + * @return boolean + */ + 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) { @@ -5392,52 +3725,13 @@ public BType findCompatibleType(BType type) { } 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) { - return !type.isNullable(); - } - 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.getFlags(), 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.getFlags(), Flags.ISOLATED); + return SemTypes.isSubtype(semTypeCtx, bType.semType(), + SemTypes.union(PredefinedType.VAL_READONLY, createIsolatedObject(semTypeCtx))); } private boolean isImmutable(BType type) { @@ -5470,7 +3764,15 @@ BType getTypeWithoutNil(BType type) { } 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) { @@ -5535,14 +3837,7 @@ 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) { @@ -5600,7 +3895,7 @@ 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; @@ -5720,21 +4015,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)); } } @@ -6173,4 +4454,8 @@ public boolean isMappingConstructorCompatibleType(BType type) { 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 91ed3606c8eb..b321df56e737 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 @@ -142,8 +142,8 @@ public class SymbolTable { public final BIntersectionType anyAndReadonly; public BUnionType anyAndReadonlyOrError; - public final BType semanticError = new BType(TypeTags.SEMANTIC_ERROR, null); - public final BType nullSet = new BType(TypeTags.NULL_SET, 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); 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 733df8ec357d..9e432d722ed3 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 @@ -149,6 +149,14 @@ 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 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 64ac517a491f..77659e810032 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 @@ -21,7 +21,6 @@ import io.ballerina.types.SemType; import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.ConstrainedType; -import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -79,7 +78,6 @@ public SemType semType() { return PredefinedType.FUTURE; } - SemType constraintSemtype = SemTypeHelper.semType(constraint); - return SemTypes.futureContaining(env, constraintSemtype); + return SemTypes.futureContaining(env, constraint.semType()); } } 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 348bf14f627b..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 @@ -22,7 +22,6 @@ import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.IntersectionType; import org.ballerinalang.model.types.TypeKind; -import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; 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; @@ -135,6 +134,13 @@ 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 @@ -145,7 +151,7 @@ public SemType semType() { private SemType computeResultantIntersection() { SemType t = PredefinedType.VAL; for (BType constituentType : this.getConstituentTypes()) { - t = SemTypes.intersect(t, SemTypeHelper.semType(constituentType)); + t = SemTypes.intersect(t, constituentType.semType()); } // TODO: this is a temporary workaround to propagate effective typeIds 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 e8058c686293..37f600c2831d 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 @@ -189,6 +189,14 @@ 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()) { 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 dc8a7f7d29da..eca371bf534d 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 @@ -98,6 +98,14 @@ 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 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 7bd041588c01..6822a51bb97d 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 @@ -19,7 +19,6 @@ import io.ballerina.types.PredefinedType; import org.ballerinalang.model.types.NullType; -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; @@ -40,9 +39,4 @@ public class BNilType extends BType implements NullType { 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/BObjectType.java b/compiler/ballerina-lang/src/main/java/org/wso2/ballerinalang/compiler/semantics/model/types/BObjectType.java index 3b90cb509b2e..0c926259eba8 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 @@ -105,8 +105,12 @@ private boolean hasTypeHoles() { return fields.values().stream().anyMatch(field -> field.type instanceof BNoType); } - public SemType semTypeIgnoringTypeIds() { - return semTypeInner(); + /** + * When the type is mutated we need to reset the definition used for the semType. + */ + @Override + public void resetSemType() { + od = null; } @Override 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 a44bd3fabb4f..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 @@ -18,7 +18,6 @@ package org.wso2.ballerinalang.compiler.semantics.model.types; import io.ballerina.types.SemType; -import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; 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; @@ -64,6 +63,6 @@ public R accept(BTypeVisitor visitor, T t) { @Override public SemType semType() { - return SemTypeHelper.semType(this.paramValueType); + return this.paramValueType.semType(); } } 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 194d2426ca49..d88c2f7f01bc 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 @@ -144,6 +144,14 @@ private boolean hasTypeHoles() { 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 @@ -171,8 +179,8 @@ public SemType semType() { SemType ty = bType.semType(); if (ty == null || NEVER.equals(ty)) { if (!optional) { - // if there is a non-optional field with `never` type(BType Component + SemType Component), - // it is not possible to create a value. Hence, the whole record type is considered as `never`. + // 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; } 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 c993b2642c4b..287c19a3783b 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 @@ -24,7 +24,6 @@ import io.ballerina.types.definition.StreamDefinition; import org.ballerinalang.model.types.StreamType; import org.ballerinalang.model.types.Type; -import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -79,6 +78,14 @@ 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) { @@ -90,8 +97,6 @@ public SemType semType() { } d = new StreamDefinition(); - SemType valueTy = SemTypeHelper.semType(constraint); - SemType completionTy = SemTypeHelper.semType(completionType); - return d.define(env, valueTy, completionTy); + return d.define(env, constraint.semType(), completionType.semType()); } } 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 6aa2f4d39425..0d0fd7e2faf4 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 @@ -258,6 +258,14 @@ private boolean hasTypeHoles() { 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 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 d95ec0e38274..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 @@ -22,7 +22,6 @@ import org.ballerinalang.model.Name; import org.ballerinalang.model.types.TypeKind; import org.ballerinalang.model.types.ValueType; -import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.util.Names; @@ -107,7 +106,7 @@ public BType getReturnType() { } public boolean isNullable() { - return Core.containsNil(SemTypeHelper.semType(this)); + return Core.containsNil(semType()); } public R accept(BTypeVisitor visitor, T t) { @@ -157,6 +156,13 @@ public void setFlags(long 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() { } /** 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 e7dae3e13d7e..df9b8787bc04 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 @@ -22,7 +22,6 @@ import io.ballerina.types.SemType; import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.ConstrainedType; -import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; import org.wso2.ballerinalang.compiler.semantics.model.TypeVisitor; import org.wso2.ballerinalang.compiler.semantics.model.symbols.BTypeSymbol; import org.wso2.ballerinalang.compiler.util.TypeTags; @@ -76,7 +75,6 @@ public SemType semType() { return PredefinedType.TYPEDESC; } - SemType constraintSemtype = SemTypeHelper.semType(constraint); - return SemTypes.typedescContaining(env, constraintSemtype); + return SemTypes.typedescContaining(env, constraint.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 6d2b4f3c3c29..a9451a743286 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 @@ -24,7 +24,6 @@ import io.ballerina.types.SemTypes; import org.ballerinalang.model.types.TypeKind; import org.ballerinalang.model.types.UnionType; -import org.wso2.ballerinalang.compiler.semantics.analyzer.SemTypeHelper; 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; @@ -53,10 +52,9 @@ public class BUnionType extends BType implements UnionType { private String cachedToString; protected LinkedHashSet memberTypes; - public LinkedHashSet memberSemTypes; - public Boolean isAnyData = null; - public Boolean isPureType = null; + private LinkedHashSet memberSemTypes; + public boolean isCyclic = false; @@ -490,7 +488,7 @@ public void populateMemberSemTypes(boolean ignoreTypeIds) { } this.memberSemTypes = memberSemTypes; - this.semType = null; // reset cached sem-type if exists + this.resetSemType(); } private void populateMemberSemTypes(BType memberType, LinkedHashSet memberSemTypes, @@ -507,15 +505,17 @@ private void populateMemberSemTypes(BType memberType, LinkedHashSet mem return; } - SemType s = SemTypeHelper.semType(memberType, ignoreTypeIds); + SemType s = memberType.semType(); if (!Core.isNever(s)) { memberSemTypes.add(s); } } - public SemType semTypeIgnoringTypeIds() { - populateMemberSemTypes(true); - return computeResultantUnion(memberSemTypes); + /** + * When the type is mutated we need to reset resolved semType. + */ + public void resetSemType() { + this.semType = null; } @Override 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/spotbugs-exclude.xml b/semtypes/spotbugs-exclude.xml index 3fd8e22b45ca..3c1e663a6eab 100644 --- a/semtypes/spotbugs-exclude.xml +++ b/semtypes/spotbugs-exclude.xml @@ -81,4 +81,11 @@ + + + + + + + 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/Context.java b/semtypes/src/main/java/io/ballerina/types/Context.java index 6adbc0f212c1..d0da307ccf5c 100644 --- a/semtypes/src/main/java/io/ballerina/types/Context.java +++ b/semtypes/src/main/java/io/ballerina/types/Context.java @@ -33,6 +33,7 @@ public final class Context { 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<>(); @@ -42,6 +43,8 @@ public final class Context { SemType anydataMemo; SemType jsonMemo; SemType cloneableMemo; + SemType isolatedObjectMemo; + SemType serviceObjectMemo; private Context(Env env) { this.env = env; diff --git a/semtypes/src/main/java/io/ballerina/types/Core.java b/semtypes/src/main/java/io/ballerina/types/Core.java index ce9fb7353a98..82ab6e91c296 100644 --- a/semtypes/src/main/java/io/ballerina/types/Core.java +++ b/semtypes/src/main/java/io/ballerina/types/Core.java @@ -19,14 +19,18 @@ 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; @@ -34,6 +38,7 @@ import java.math.BigDecimal; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Optional; @@ -66,6 +71,8 @@ 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. @@ -74,9 +81,6 @@ */ public final class Core { - private static final boolean SEM_ALL_TEST = - Boolean.parseBoolean(System.getProperty("ballerina.semtype.all.types.test")); - public static CellAtomicType cellAtomType(Atom atom) { return (CellAtomicType) ((TypeAtom) atom).atomicType(); } @@ -431,6 +435,158 @@ public static SemType listMemberTypeInnerVal(Context cx, SemType t, SemType k) { } } + 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) { @@ -758,6 +914,30 @@ public static SemType createCloneable(Context context) { 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)) { @@ -770,4 +950,52 @@ public static SemType createBasicSemType(BasicTypeCode typeCode, SubtypeData sub 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/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/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 index 2601dda9c64d..037c9deac740 100644 --- a/semtypes/src/main/java/io/ballerina/types/SemTypes.java +++ b/semtypes/src/main/java/io/ballerina/types/SemTypes.java @@ -105,10 +105,18 @@ 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); } diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNode.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNode.java index ee64fef97e73..c4c61efede1a 100644 --- a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNode.java +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNode.java @@ -34,9 +34,9 @@ static BddNode create(Atom atom, Bdd left, Bdd middle, Bdd right) { } private static boolean isSimpleNode(Bdd left, Bdd middle, Bdd right) { - return left instanceof AllOrNothingSubtype leftNode && leftNode.isAllSubtype() && - middle instanceof AllOrNothingSubtype middleNode && middleNode.isNothingSubtype() && - right instanceof AllOrNothingSubtype rightNode && rightNode.isNothingSubtype(); + return left instanceof BddAllOrNothing leftNode && leftNode.isAll() && + middle instanceof BddAllOrNothing middleNode && middleNode.isNothing() && + right instanceof BddAllOrNothing rightNode && rightNode.isNothing(); } Atom atom(); diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeImpl.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeImpl.java index 760503d64efd..f2bea68b01ef 100644 --- a/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeImpl.java +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/BddNodeImpl.java @@ -29,6 +29,6 @@ * @param right path that include this node's atom negatively * @since 2201.10.0 */ -record BddNodeImpl(Atom atom, Bdd left, Bdd middle, Bdd right) implements BddNode { +public record BddNodeImpl(Atom atom, Bdd left, Bdd middle, Bdd right) implements BddNode { } diff --git a/semtypes/src/main/java/io/ballerina/types/subtypedata/Range.java b/semtypes/src/main/java/io/ballerina/types/subtypedata/Range.java index 4fd69ad7cd9f..f34ca9f3a501 100644 --- a/semtypes/src/main/java/io/ballerina/types/subtypedata/Range.java +++ b/semtypes/src/main/java/io/ballerina/types/subtypedata/Range.java @@ -31,7 +31,7 @@ public Range(long min, long max) { this.max = max; } - public static Range from(int min, int max) { + public static Range from(long min, long max) { return new Range(min, max); } 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 4ccd6c1caf00..2368ce607ccf 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/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/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/object/ObjectEquivalencyNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/object/ObjectEquivalencyNegativeTest.java index c7d745878f0e..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 @@ -32,8 +32,6 @@ public class ObjectEquivalencyNegativeTest { public void testEquivalenceOfObjectsInSamePackage() { CompileResult compileResult = BCompileUtil.compile("test-src/object/object-equivalency-01-negative.bal"); int i = 0; - BAssertUtil.validateError(compileResult, i++, - "incompatible types: 'employee01' cannot be cast to 'person01'", 24, 18); BAssertUtil.validateError(compileResult, i++, "incompatible types: 'employee02' cannot be cast to 'person02'", 51, 18); BAssertUtil.validateError(compileResult, i++, @@ -42,10 +40,6 @@ public void testEquivalenceOfObjectsInSamePackage() { "incompatible types: 'employee05' cannot be cast to 'person05'", 145, 18); BAssertUtil.validateError(compileResult, i++, "incompatible types: 'employee06' cannot be cast to 'person06'", 175, 18); - BAssertUtil.validateError(compileResult, i++, - "incompatible types: 'employee08' cannot be cast to 'person08'", 284, 18); - BAssertUtil.validateError(compileResult, i++, - "incompatible types: 'employee09' cannot be cast to 'person09'", 341, 18); BAssertUtil.validateError(compileResult, i++, "incompatible types: expected 'ObjWithRemoteMethod', found 'NonClientObj'", 460, 29); BAssertUtil.validateError(compileResult, i++, 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/statements/ifelse/TypeGuardTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/statements/ifelse/TypeGuardTest.java index ef25c155cc9f..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 @@ -95,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); } 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/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/finite/FiniteTypeNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/types/finite/FiniteTypeNegativeTest.java index 68830e86417b..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); 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 bd0a4136aa88..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 @@ -79,10 +79,6 @@ public void testNegativeCases() { 16); BAssertUtil.validateError(negativeResult, index++, "incompatible types: expected 'string', found 'int'", 60, 16); - BAssertUtil.validateError(negativeResult, index++, "incompatible types: 'CustomerTable' cannot be" + - " cast to 'CustomerEmptyKeyedTbl'", 77, 34); - BAssertUtil.validateError(negativeResult, index++, "incompatible types: 'CustomerTable' cannot be cast to " + - "'table'", 83, 20); Assert.assertEquals(negativeResult.getErrorCount(), index); } 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/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/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/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/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;