diff --git a/src/language/node_processor/FunctionProcessor.hpp b/src/language/node_processor/FunctionProcessor.hpp index 16e65e6e1e1ca63a7e1fa57e109856b3da25610a..2184699f5013a7d27c17f5ede3987557daf0f99d 100644 --- a/src/language/node_processor/FunctionProcessor.hpp +++ b/src/language/node_processor/FunctionProcessor.hpp @@ -57,7 +57,13 @@ class FunctionExpressionProcessor final : public INodeProcessor } else if constexpr (std::is_same_v<ExpressionValueType, ZeroType>) { return ReturnType{ZeroType::zero}; } else if constexpr (std::is_convertible_v<ExpressionValueType, ReturnType>) { - return static_cast<ReturnType>(std::get<ExpressionValueType>(m_function_expression.execute(exec_policy))); + const ExpressionValueType& v = std::get<ExpressionValueType>(m_function_expression.execute(exec_policy)); + if constexpr (std::is_same_v<ReturnType, uint64_t> and std::is_same_v<ExpressionValueType, int64_t>) { + if (v < 0) { + throw std::domain_error("trying to convert negative value (" + std::to_string(v) + ")"); + } + } + return static_cast<ReturnType>(v); } else if constexpr (std::is_arithmetic_v<ExpressionValueType> and (is_tiny_vector_v<ReturnType> or is_tiny_matrix_v<ReturnType>)) { static_assert(ReturnType::Dimension == 1, "invalid conversion"); @@ -105,26 +111,41 @@ class FunctionProcessor : public INodeProcessor ExecutionPolicy context_exec_policy{exec_policy, context}; if (m_argument_converters.size() == 1) { - m_argument_converters[0]->convert(context_exec_policy, m_argument_node.execute(context_exec_policy)); + try { + m_argument_converters[0]->convert(context_exec_policy, m_argument_node.execute(context_exec_policy)); + } + catch (std::domain_error& e) { + throw ParseError(e.what(), m_argument_node.begin()); + } } else { AggregateDataVariant argument_values{ std::get<AggregateDataVariant>(m_argument_node.execute(context_exec_policy))}; for (size_t i = 0; i < m_argument_converters.size(); ++i) { - m_argument_converters[i]->convert(context_exec_policy, std::move(argument_values[i])); + try { + m_argument_converters[i]->convert(context_exec_policy, std::move(argument_values[i])); + } + catch (std::domain_error& e) { + throw ParseError(e.what(), m_argument_node.children[i]->begin()); + } } } - if (m_function_expression_processors.size() == 1) { - return m_function_expression_processors[0]->execute(context_exec_policy); - } else { - std::vector<DataVariant> list_values; - list_values.reserve(m_function_expression_processors.size()); + try { + if (m_function_expression_processors.size() == 1) { + return m_function_expression_processors[0]->execute(context_exec_policy); + } else { + std::vector<DataVariant> list_values; + list_values.reserve(m_function_expression_processors.size()); - for (auto& function_expression_processor : m_function_expression_processors) { - list_values.emplace_back(function_expression_processor->execute(context_exec_policy)); + for (auto& function_expression_processor : m_function_expression_processors) { + list_values.emplace_back(function_expression_processor->execute(context_exec_policy)); + } + return AggregateDataVariant{std::move(list_values)}; } - return AggregateDataVariant{std::move(list_values)}; + } + catch (std::domain_error& e) { + throw ParseError(e.what(), m_argument_node.begin()); } } diff --git a/tests/test_FunctionProcessor.cpp b/tests/test_FunctionProcessor.cpp index 5c72fd0d7b96a4919490b6707bde1bef03aa0b28..aac10423f5d2b7db6fc2d17e4fc802e106f5a4dd 100644 --- a/tests/test_FunctionProcessor.cpp +++ b/tests/test_FunctionProcessor.cpp @@ -46,6 +46,26 @@ REQUIRE(value == expected_value); \ } +#define CHECK_FUNCTION_EVALUATION_THROWS(data, error_msg) \ + { \ + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTModulesImporter{*ast}; \ + ASTNodeTypeCleaner<language::import_instruction>{*ast}; \ + \ + ASTSymbolTableBuilder{*ast}; \ + ASTNodeDataTypeBuilder{*ast}; \ + \ + ASTNodeDeclarationToAffectationConverter{*ast}; \ + ASTNodeTypeCleaner<language::var_declaration>{*ast}; \ + ASTNodeTypeCleaner<language::fct_declaration>{*ast}; \ + \ + ASTNodeExpressionBuilder{*ast}; \ + ExecutionPolicy exec_policy; \ + REQUIRE_THROWS_WITH(ast->execute(exec_policy), error_msg); \ + } + // clazy:excludeall=non-pod-global-static TEST_CASE("FunctionProcessor", "[language]") @@ -730,4 +750,54 @@ let x:R, x = g(f(3)); A33(2, 0) * A33(0, 2) - A22(1, 1)}); } } + + SECTION("errors") + { + SECTION("negative value to N") + { + SECTION("single N argument") + { + std::string_view data = R"( +let f : N -> N, n -> 2; +f(2); +f(-1); +)"; + + CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-1)"); + } + + SECTION("multiple N arguments") + { + std::string_view data = R"( +let f : R*N -> R, (n,x) -> n*x; +f(-2, 3); +f(-1,-4); +)"; + + CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-4)"); + } + + SECTION("single N return value") + { + std::string_view data = R"( +let f : N*N -> N, (m,n) -> m-n; +f(2, 1); +f(1, 3); +)"; + + CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-2)"); + } + + SECTION("compound N return value") + { + std::string_view data = R"( +let f : N*N -> N*N, (m,n) -> (m+n, m-n); +f(3, 2); +f(1, 4); +)"; + + CHECK_FUNCTION_EVALUATION_THROWS(data, "trying to convert negative value (-3)"); + } + } + } }