From af2d4f192eac94e626b0729e54ef337464c910c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Tue, 1 Oct 2024 08:35:33 +0200 Subject: [PATCH 1/2] Added tests for typechecking function calls (crashes when arg count is less than param count) --- test/models/function_calls.xml | 45 ++++++++++++++++++ test/test_typechecker.cpp | 83 +++++++++++++++++++++++++++++++++- 2 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 test/models/function_calls.xml diff --git a/test/models/function_calls.xml b/test/models/function_calls.xml new file mode 100644 index 00000000..4df1ec76 --- /dev/null +++ b/test/models/function_calls.xml @@ -0,0 +1,45 @@ + + + + int v; + +bool enabled(int i) { + return (i>0); +} + + + system P; + + + simulate[<=1;2] { v } : 1 : enabled(1) + + +
≥ 0.025 (95% CI)
+ + 0.0,-1.0 + + + +
+
+ + simulate[<=1;2] { v } : 1 : enabled() + + +
+
diff --git a/test/test_typechecker.cpp b/test/test_typechecker.cpp index eb924a93..42ac1bf3 100644 --- a/test/test_typechecker.cpp +++ b/test/test_typechecker.cpp @@ -473,4 +473,85 @@ TEST_CASE("Nested structs") CHECK(warns.size() == 0); auto errs = doc->get_errors(); CHECK(errs.size() == 1); -} \ No newline at end of file +} + +TEST_CASE("Function calls in queries") +{ + auto doc = read_document("function_calls.xml"); + auto builder = std::make_unique(*doc); + SUBCASE("Correct") + { + auto res = parseProperty("simulate[<=5;3] { v, enabled(1) } : 2 : enabled(1)", builder.get()); + REQUIRE(res == 0); + builder->typecheck(); + const auto expr = builder->getQuery(); + REQUIRE(expr.get_size() == 7); + CHECK(expr.get(0).get_value() == 3); ///< max runs + CHECK(expr.get(1).get_value() == 1); ///< bound kind of time + CHECK(expr.get(2).get_value() == 5); ///< time bound + // CHECK(expr.get(3)); + // CHECK(expr.get(4)); + // CHECK(expr.get(5)); + CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + } + SUBCASE("Predicate misses argument") + { + auto res = parseProperty("simulate[<=5;3] { v, enabled(1) } : 2 : enabled()", builder.get()); + REQUIRE(res == 0); + builder->typecheck(); + const auto expr = builder->getQuery(); + REQUIRE(expr.get_size() == 7); + CHECK(expr.get(0).get_value() == 3); ///< max runs + CHECK(expr.get(1).get_value() == 1); ///< bound kind of time + CHECK(expr.get(2).get_value() == 5); ///< time bound + // CHECK(expr.get(3)); + // CHECK(expr.get(4)); + // CHECK(expr.get(5)); + CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + } + SUBCASE("Monitored expression misses argument") + { + auto res = parseProperty("simulate[<=5;3] { v, enabled() } : 2 : enabled(1)", builder.get()); + REQUIRE(res == 0); + builder->typecheck(); + auto expr = builder->getQuery(); + REQUIRE(expr.get_size() == 7); + CHECK(expr.get(0).get_value() == 3); ///< max runs + CHECK(expr.get(1).get_value() == 1); ///< bound kind of time + CHECK(expr.get(2).get_value() == 5); ///< time bound + // CHECK(expr.get(3)); + // CHECK(expr.get(4)); + // CHECK(expr.get(5)); + CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + } + SUBCASE("Missing arguments in both calls") + { + auto res = parseProperty("simulate[<=5;3] { v, enabled() } : 2 : enabled()", builder.get()); + REQUIRE(res == 0); + builder->typecheck(); + auto expr = builder->getQuery(); + REQUIRE(expr.get_size() == 7); + CHECK(expr.get(0).get_value() == 3); ///< max runs + CHECK(expr.get(1).get_value() == 1); ///< bound kind of time + CHECK(expr.get(2).get_value() == 5); ///< time bound + // CHECK(expr.get(3)); + // CHECK(expr.get(4)); + // CHECK(expr.get(5)); + CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + } + SUBCASE("Non-existent fn") + { + auto res = parseProperty("simulate[<=5;3] { v, non_existent() }:2:non_existent()", builder.get()); + REQUIRE(res == 0); + builder->typecheck(); + auto expr = builder->getQuery(); + REQUIRE(expr.get_size() == 7); + CHECK(expr.get(0).get_value() == 3); ///< max runs + CHECK(expr.get(1).get_value() == 1); ///< bound kind of time + CHECK(expr.get(2).get_value() == 5); ///< time bound + // CHECK(expr.get(3)); + // CHECK(expr.get(4)); + // CHECK(expr.get(5)); + CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + } +} From d5bf0ef2df8e879dcd2f1160630d7239b7e11f5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marius=20Miku=C4=8Dionis?= Date: Fri, 14 Feb 2025 14:28:47 +0100 Subject: [PATCH 2/2] Unfinished work --- src/ExpressionBuilder.cpp | 14 ++++---- src/typechecker.cpp | 16 +++++---- test/models/function_calls.xml | 28 +++++++++------- test/test_typechecker.cpp | 61 ++++++++++++++++++++++------------ 4 files changed, 74 insertions(+), 45 deletions(-) diff --git a/src/ExpressionBuilder.cpp b/src/ExpressionBuilder.cpp index e07181a9..205d412f 100644 --- a/src/ExpressionBuilder.cpp +++ b/src/ExpressionBuilder.cpp @@ -341,17 +341,19 @@ void ExpressionBuilder::expr_call_end(uint32_t n) switch (id.get_type().get_kind()) { case FUNCTION_EXTERNAL: case FUNCTION: - if (expr.size() != id.get_type().size()) { - handle_error(TypeException{"$Wrong_number_of_arguments"}); - } + if (expr.size() < id.get_type().size()) + handle_error(TypeException{"$Too_few_arguments_for_function_call"}); + if (expr.size() > id.get_type().size()) + handle_error(TypeException{"$Too_many_arguments_for_function_call"}); e = expression_t::create_nary(id.get_type().get_kind() == FUNCTION ? FUN_CALL : FUN_CALL_EXT, expr, position, id.get_type()[0]); break; case PROCESS_SET: - if (expr.size() - 1 != id.get_type().size()) { - handle_error(TypeException{"$Wrong_number_of_arguments"}); - } + if (expr.size() - 1 < id.get_type().size()) + handle_error(TypeException{"$Too_few_arguments_for_template_instantiation"}); + if (expr.size() - 1 > id.get_type().size()) + handle_error(TypeException{"$Too_many_arguments_for_template_instantiation"}); instance = static_cast(id.get_symbol().get_data()); /* Process set lookups are represented as expressions indexing diff --git a/src/typechecker.cpp b/src/typechecker.cpp index 553f3463..07ea9a5d 100644 --- a/src/typechecker.cpp +++ b/src/typechecker.cpp @@ -2129,12 +2129,16 @@ bool TypeChecker::checkExpression(expression_t expr) checkExpression(expr[0]); bool result = true; - type_t type = expr[0].get_type(); - size_t parameters = type.size() - 1; - for (uint32_t i = 0; i < parameters; i++) { - type_t parameter = type[i + 1]; - expression_t argument = expr[i + 1]; - result &= checkParameterCompatible(parameter, argument); + type_t fn_type = expr[0].get_type(); // [ret_type, arg1_type, ..., argn_type] + size_t param_count = fn_type.size() - 1; + for (uint32_t i = 1; i < param_count; ++i) { + if (i >= expr.get_size()) { + handleError(expr, "$Too_few_function_arguments"); + break; + } + type_t param_type = fn_type[i]; + expression_t argument = expr[i]; + result &= checkParameterCompatible(param_type, argument); } return result; } diff --git a/test/models/function_calls.xml b/test/models/function_calls.xml index 4df1ec76..3846e805 100644 --- a/test/models/function_calls.xml +++ b/test/models/function_calls.xml @@ -26,20 +26,24 @@ bool enabled(int i) { system P; - simulate[<=1;2] { v } : 1 : enabled(1) - - -
≥ 0.025 (95% CI)
- - 0.0,-1.0 - - - -
+ simulate[<=5;3] { v, enabled(1) } : 2 : enabled(1) + Good call, should pass
- simulate[<=1;2] { v } : 1 : enabled() - + simulate[<=5;3] { v, enabled(1) } : 2 : enabled() + Call is missing argument, should give meaningful error and not crash. + + + simulate[<=5;3] { v, enabled() } : 2 : enabled(1) + Call is missing argument, should give meaningful error and not crash. + + + simulate[<=5;3] { v, enabled() } : 2 : enabled() + Both calls are missing arguments, should give error message and not crash + + + simulate[<=5;3] { v, non_existent() } : 2 : non_existent() + Chould give meaningfull error message and not crash
diff --git a/test/test_typechecker.cpp b/test/test_typechecker.cpp index 42ac1bf3..f29a977b 100644 --- a/test/test_typechecker.cpp +++ b/test/test_typechecker.cpp @@ -478,13 +478,19 @@ TEST_CASE("Nested structs") TEST_CASE("Function calls in queries") { auto doc = read_document("function_calls.xml"); - auto builder = std::make_unique(*doc); + const auto& errs = doc->get_errors(); + REQUIRE_MESSAGE(errs.empty(), errs.front().msg); + auto builder = std::make_unique(*doc); + const auto& queries = doc->get_queries(); + REQUIRE(queries.size() == 5); SUBCASE("Correct") { - auto res = parseProperty("simulate[<=5;3] { v, enabled(1) } : 2 : enabled(1)", builder.get()); - REQUIRE(res == 0); - builder->typecheck(); - const auto expr = builder->getQuery(); + const auto& query = *queries.begin(); + builder->parse(query.formula.c_str(), query.location, query.options); + REQUIRE_MESSAGE(errs.empty(), errs.front().msg); + /* + REQUIRE_MESSAGE(props.size() == prop_count + 1, "Should contain one more property"); + const auto& expr = std::next(props.begin(), prop_count)->intermediate; REQUIRE(expr.get_size() == 7); CHECK(expr.get(0).get_value() == 3); ///< max runs CHECK(expr.get(1).get_value() == 1); ///< bound kind of time @@ -493,13 +499,16 @@ TEST_CASE("Function calls in queries") // CHECK(expr.get(4)); // CHECK(expr.get(5)); CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + */ } SUBCASE("Predicate misses argument") { - auto res = parseProperty("simulate[<=5;3] { v, enabled(1) } : 2 : enabled()", builder.get()); - REQUIRE(res == 0); - builder->typecheck(); - const auto expr = builder->getQuery(); + const auto& query = *std::next(queries.begin(), 1); + builder->parse(query.formula.c_str(), query.location, query.options); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + /* + REQUIRE_MESSAGE(props.size() == prop_count + 1, "Should contain one more property"); + const auto& expr = std::next(props.begin(), prop_count)->intermediate; REQUIRE(expr.get_size() == 7); CHECK(expr.get(0).get_value() == 3); ///< max runs CHECK(expr.get(1).get_value() == 1); ///< bound kind of time @@ -508,13 +517,16 @@ TEST_CASE("Function calls in queries") // CHECK(expr.get(4)); // CHECK(expr.get(5)); CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + */ } SUBCASE("Monitored expression misses argument") { - auto res = parseProperty("simulate[<=5;3] { v, enabled() } : 2 : enabled(1)", builder.get()); - REQUIRE(res == 0); - builder->typecheck(); - auto expr = builder->getQuery(); + const auto& query = *std::next(queries.begin(), 2); + builder->parse(query.formula.c_str(), query.location, query.options); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + /* + REQUIRE_MESSAGE(props.size() == prop_count + 1, "Should contain one more property"); + const auto& expr = std::next(props.begin(), prop_count)->intermediate; REQUIRE(expr.get_size() == 7); CHECK(expr.get(0).get_value() == 3); ///< max runs CHECK(expr.get(1).get_value() == 1); ///< bound kind of time @@ -523,13 +535,16 @@ TEST_CASE("Function calls in queries") // CHECK(expr.get(4)); // CHECK(expr.get(5)); CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + */ } SUBCASE("Missing arguments in both calls") { - auto res = parseProperty("simulate[<=5;3] { v, enabled() } : 2 : enabled()", builder.get()); - REQUIRE(res == 0); - builder->typecheck(); - auto expr = builder->getQuery(); + const auto& query = *std::next(queries.begin(), 3); + builder->parse(query.formula.c_str(), query.location, query.options); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + /* + REQUIRE_MESSAGE(props.size() == prop_count + 1, "Should contain one more property"); + const auto& expr = std::next(props.begin(), prop_count)->intermediate; REQUIRE(expr.get_size() == 7); CHECK(expr.get(0).get_value() == 3); ///< max runs CHECK(expr.get(1).get_value() == 1); ///< bound kind of time @@ -538,13 +553,16 @@ TEST_CASE("Function calls in queries") // CHECK(expr.get(4)); // CHECK(expr.get(5)); CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + */ } SUBCASE("Non-existent fn") { - auto res = parseProperty("simulate[<=5;3] { v, non_existent() }:2:non_existent()", builder.get()); - REQUIRE(res == 0); - builder->typecheck(); - auto expr = builder->getQuery(); + const auto& query = *std::next(queries.begin(), 4); + builder->parse(query.formula.c_str(), query.location, query.options); + CHECK_MESSAGE(errs.empty(), errs.front().msg); + /* + REQUIRE_MESSAGE(props.size() == prop_count + 1, "Should contain one more property"); + const auto& expr = std::next(props.begin(), prop_count)->intermediate; REQUIRE(expr.get_size() == 7); CHECK(expr.get(0).get_value() == 3); ///< max runs CHECK(expr.get(1).get_value() == 1); ///< bound kind of time @@ -553,5 +571,6 @@ TEST_CASE("Function calls in queries") // CHECK(expr.get(4)); // CHECK(expr.get(5)); CHECK(expr.get(6).get_value() == 2); ///< number of satisfying runs + */ } }