diff --git a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java index 59cf113480c3..43219f96ad1c 100644 --- a/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java +++ b/compiler/ballerina-lang/src/main/java/org/ballerinalang/util/diagnostic/DiagnosticErrorCode.java @@ -805,11 +805,13 @@ public enum DiagnosticErrorCode implements DiagnosticCode { SEQUENCE_VARIABLE_CAN_BE_USED_IN_SINGLE_ELEMENT_LIST_CTR_OR_FUNC_INVOCATION( "BCE4047", "seq.var.used.in.single.element.list.ctr.or.func.invocation"), SEQ_ARG_FOLLOWED_BY_ANOTHER_SEQ_ARG("BCE4048", "seq.arg.followed.by.another.seq.arg"), - QUERY_CONSTRUCT_TYPES_CANNOT_BE_USED_WITH_COLLECT("BCE4049", "query.construct.types.cannot.be.used.with.collect"), VARIABLE_IS_SEQUENCED_MORE_THAN_ONCE("BCE4050", "variable.is.sequenced.more.than.once"), INVALID_GROUPING_KEY_TYPE("BCE4051", "invalid.grouping.key.type"), - NAMED_ARG_NOT_ALLOWED_FOR_REST_PARAM("BCE4052", "named.arg.not.allowed.for.rest.param") + NAMED_ARG_NOT_ALLOWED_FOR_REST_PARAM("BCE4052", "named.arg.not.allowed.for.rest.param"), + CANNOT_USE_ALTERNATE_WAIT_ACTION_WITHIN_MULTIPLE_WAIT_ACTION("BCE4053", + "cannot.use.alternate.wait.action.within.multiple.wait.action"), + EXPRESSION_OF_FUTURE_TYPE_EXPECTED("BCE4054", "future.expression.expected") ; private String diagnosticId; 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 96fd79e19edb..85d990184299 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 @@ -4721,7 +4721,12 @@ private void checkTypesForRecords(BLangWaitForAllExpr waitExpr, AnalyzerData dat for (BLangWaitForAllExpr.BLangWaitKeyValue keyVal : rhsFields) { String key = keyVal.key.value; - if (!lhsFields.containsKey(key)) { + BLangExpression valueExpr = keyVal.valueExpr; + if (valueExpr != null && isBinaryBitwiseOperatorExpr(valueExpr)) { + dlog.error(valueExpr.pos, + DiagnosticErrorCode.CANNOT_USE_ALTERNATE_WAIT_ACTION_WITHIN_MULTIPLE_WAIT_ACTION); + data.resultType = symTable.semanticError; + } else if (!lhsFields.containsKey(key)) { // Check if the field is sealed if so you cannot have dynamic fields if (((BRecordType) Types.getReferredType(data.expType)).sealed) { dlog.error(waitExpr.pos, DiagnosticErrorCode.INVALID_FIELD_NAME_RECORD_LITERAL, key, data.expType); @@ -4746,6 +4751,17 @@ private void checkTypesForRecords(BLangWaitForAllExpr waitExpr, AnalyzerData dat } } + private boolean isBinaryBitwiseOperatorExpr(BLangExpression valueExpr) { + if (valueExpr.getKind() == NodeKind.GROUP_EXPR) { + return isBinaryBitwiseOperatorExpr(((BLangGroupExpr) valueExpr).expression); + } + if (valueExpr.getKind() == NodeKind.BINARY_EXPR + && ((BLangBinaryExpr) valueExpr).opKind == OperatorKind.BITWISE_OR) { + return true; + } + return false; + } + private void checkMissingReqFieldsForWait(BRecordType type, List keyValPairs, Location pos) { type.fields.values().forEach(field -> { @@ -4783,7 +4799,14 @@ private void setEventualTypeForExpression(BLangExpression expression, if (isSimpleWorkerReference(expression, data)) { return; } - BFutureType futureType = (BFutureType) expression.expectedType; + + BType expectedType = expression.expectedType; + if (expectedType.tag != TypeTags.FUTURE) { + dlog.error(expression.pos, DiagnosticErrorCode.EXPRESSION_OF_FUTURE_TYPE_EXPECTED, expectedType); + return; + } + + BFutureType futureType = (BFutureType) expectedType; BType currentType = futureType.constraint; if (types.containsErrorType(currentType)) { return; diff --git a/compiler/ballerina-lang/src/main/resources/compiler.properties b/compiler/ballerina-lang/src/main/resources/compiler.properties index 6f1afc4226e4..a31fa2c4899c 100644 --- a/compiler/ballerina-lang/src/main/resources/compiler.properties +++ b/compiler/ballerina-lang/src/main/resources/compiler.properties @@ -1971,6 +1971,12 @@ error.empty.regexp.string.disallowed=\ error.unsupported.empty.character.class=\ empty character class disallowed +error.cannot.use.alternate.wait.action.within.multiple.wait.action=\ + cannot use an alternate wait action within a multiple wait action + +error.future.expression.expected=\ + expected an expression of type ''future'', found ''{0}'' + error.cyclic.type.reference.not.yet.supported=\ cyclic type reference not yet supported for ''{0}'' diff --git a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java index ef3f7542d88f..559420eaa2a2 100644 --- a/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java +++ b/tests/jballerina-unit-test/src/test/java/org/ballerinalang/test/worker/WaitActionsNegativeTest.java @@ -33,7 +33,6 @@ public class WaitActionsNegativeTest { public void testNegativeWorkerActions() { CompileResult resultNegative = BCompileUtil.compile("test-src/workers/wait-actions-negative.bal"); int index = 0; - Assert.assertEquals(resultNegative.getErrorCount(), 45, "Wait actions negative test error count"); BAssertUtil.validateError(resultNegative, index++, "incompatible types: expected 'future', found 'future'", 56, 22); BAssertUtil.validateError(resultNegative, index++, @@ -143,9 +142,34 @@ public void testNegativeWorkerActions() { "expression 'f2'", 90, 45); BAssertUtil.validateError(resultNegative, index++, "incompatible types: expected 'future', found 'future<(int|error)>'", 90, 54); - BAssertUtil.validateError(resultNegative, index, + BAssertUtil.validateError(resultNegative, index++, "incompatible types: expected 'string', found eventual type '(string|error)' for wait future " + "expression 'f4'", 90, 54); + BAssertUtil.validateError(resultNegative, index++, + "cannot use an alternate wait action within a multiple wait action", 115, 38); + BAssertUtil.validateError(resultNegative, index++, + "cannot use an alternate wait action within a multiple wait action", 116, 48); + BAssertUtil.validateError(resultNegative, index++, + "cannot use an alternate wait action within a multiple wait action", 117, 27); + BAssertUtil.validateError(resultNegative, index++, + "cannot use an alternate wait action within a multiple wait action", 117, 58); + BAssertUtil.validateError(resultNegative, index++, + "cannot use an alternate wait action within a multiple wait action", 119, 27); + BAssertUtil.validateError(resultNegative, index++, + "cannot use an alternate wait action within a multiple wait action", 120, 48); + BAssertUtil.validateError(resultNegative, index++, + "cannot use an alternate wait action within a multiple wait action", 121, 27); + BAssertUtil.validateError(resultNegative, index++, + "cannot use an alternate wait action within a multiple wait action", 121, 72); + BAssertUtil.validateError(resultNegative, index++, "expected an expression of type 'future'," + + " found '(future<(boolean|error)>|future<(boolean|error)>)'", 136, 38); + BAssertUtil.validateError(resultNegative, index++, "expected an expression of type 'future'," + + " found '(future|future)'", 137, 48); + BAssertUtil.validateError(resultNegative, index++, "expected an expression of type 'future'," + + " found '(future<(boolean|error)>|future<(boolean|error)>)'", 138, 27); + BAssertUtil.validateError(resultNegative, index++, "expected an expression of type 'future'," + + " found '(future|future)'", 138, 40); + Assert.assertEquals(resultNegative.getErrorCount(), index, "Wait actions negative test error count"); } @Test diff --git a/tests/jballerina-unit-test/src/test/resources/test-src/workers/wait-actions-negative.bal b/tests/jballerina-unit-test/src/test/resources/test-src/workers/wait-actions-negative.bal index 0fb820c605b0..54be86942c62 100644 --- a/tests/jballerina-unit-test/src/test/resources/test-src/workers/wait-actions-negative.bal +++ b/tests/jballerina-unit-test/src/test/resources/test-src/workers/wait-actions-negative.bal @@ -90,6 +90,54 @@ function waitForAllTest() { restRec2 result16 = wait {id: f1, name: f2, age: f4}; } +type WaitResult record {| + boolean|error producer; + boolean consumer; +|}; + +function waitForAllTest2() { + worker Producer1 returns boolean|error { + return true; + } + + worker Producer2 returns boolean|error { + return true; + } + + worker Consumer1 returns boolean { + return false; + } + + worker Consumer2 returns boolean { + return false; + } + + WaitResult res = wait {producer: Producer1|Producer2, consumer: Consumer1}; + res = wait {producer: Producer1, consumer: Consumer1|Consumer2}; + res = wait {producer: Producer1|Producer2, consumer: Consumer1|Consumer2}; + + res = wait {producer: (Producer1|(Producer2)), consumer: Consumer1}; + res = wait {producer: Producer1, consumer: (((Consumer1|Consumer2)))}; + res = wait {producer: (((((((Producer1|Producer2))))))), consumer: (Consumer1|Consumer2)}; +} + +function waitActionWithInvalidType() { + worker Producer1 returns boolean|error { + return true; + } + + worker Consumer1 returns boolean { + return false; + } + + future|future x = Producer1; + future|future y = Consumer1; + + WaitResult res = wait {producer: x, consumer: Consumer1}; + res = wait {producer: Producer1, consumer: y}; + res = wait {producer: x, consumer: y}; +} + function print(string str) { string result = str.toUpperAscii(); }