diff --git a/src/language/node_processor/AffectationProcessor.hpp b/src/language/node_processor/AffectationProcessor.hpp index c36a747289310b6e1cd393c2a2d437c015e45d50..cd377b38145e7a5d41c672cb3480d4f3be2b49b3 100644 --- a/src/language/node_processor/AffectationProcessor.hpp +++ b/src/language/node_processor/AffectationProcessor.hpp @@ -8,6 +8,8 @@ #include <utils/Exceptions.hpp> #include <utils/PugsTraits.hpp> +#include <exception> + template <typename Op> struct AffOp; @@ -18,6 +20,11 @@ struct AffOp<language::multiplyeq_op> PUGS_INLINE void eval(A& a, const B& b) { + if constexpr (std::is_same_v<uint64_t, A> and std::is_same_v<int64_t, B>) { + if (b < 0) { + throw std::domain_error("trying to affect negative value (" + std::to_string(b) + ")"); + } + } a *= b; } }; @@ -29,6 +36,11 @@ struct AffOp<language::divideeq_op> PUGS_INLINE void eval(A& a, const B& b) { + if constexpr (std::is_same_v<uint64_t, A> and std::is_same_v<int64_t, B>) { + if (b < 0) { + throw std::domain_error("trying to affect negative value (" + std::to_string(b) + ")"); + } + } a /= b; } }; @@ -40,6 +52,12 @@ struct AffOp<language::pluseq_op> PUGS_INLINE void eval(A& a, const B& b) { + if constexpr (std::is_same_v<uint64_t, A> and std::is_same_v<int64_t, B>) { + if (static_cast<int64_t>(a + b) < 0) { + throw std::domain_error("trying to affect negative value (lhs: " + std::to_string(a) + + " rhs: " + std::to_string(b) + ")"); + } + } a += b; } }; @@ -51,6 +69,12 @@ struct AffOp<language::minuseq_op> PUGS_INLINE void eval(A& a, const B& b) { + if constexpr (std::is_same_v<uint64_t, A> and std::is_same_v<int64_t, B>) { + if (static_cast<int64_t>(a - b) < 0) { + throw std::domain_error("trying to affect negative value (lhs: " + std::to_string(a) + + " rhs: " + std::to_string(b) + ")"); + } + } a -= b; } }; @@ -72,6 +96,7 @@ class AffectationExecutor final : public IAffectationExecutor { private: DataVariant& m_lhs; + ASTNode& m_node; static inline const bool m_is_defined{[] { if constexpr (std::is_same_v<std::decay_t<ValueT>, bool>) { @@ -83,7 +108,7 @@ class AffectationExecutor final : public IAffectationExecutor }()}; public: - AffectationExecutor(ASTNode& node, DataVariant& lhs) : m_lhs(lhs) + AffectationExecutor(ASTNode& node, DataVariant& lhs) : m_lhs(lhs), m_node{node} { // LCOV_EXCL_START if constexpr (not m_is_defined) { @@ -122,7 +147,13 @@ class AffectationExecutor final : public IAffectationExecutor } else { if constexpr (std::is_same_v<OperatorT, language::eq_op>) { if constexpr (std::is_convertible_v<DataT, ValueT>) { - m_lhs = static_cast<ValueT>(std::get<DataT>(rhs)); + const DataT& value = std::get<DataT>(rhs); + if constexpr (std::is_same_v<uint64_t, ValueT> and std::is_same_v<int64_t, DataT>) { + if (value < 0) { + throw std::domain_error("trying to affect negative value (" + std::to_string(value) + ")"); + } + } + m_lhs = static_cast<ValueT>(value); } else if constexpr (std::is_same_v<DataT, AggregateDataVariant>) { const AggregateDataVariant& v = std::get<AggregateDataVariant>(rhs); if constexpr (is_tiny_vector_v<ValueT>) { @@ -236,7 +267,12 @@ class AffectationProcessor final : public INodeProcessor DataVariant execute(ExecutionPolicy& exec_policy) { - m_affectation_executor->affect(exec_policy, m_rhs_node.execute(exec_policy)); + try { + m_affectation_executor->affect(exec_policy, m_rhs_node.execute(exec_policy)); + } + catch (std::domain_error& e) { + throw ParseError(e.what(), m_rhs_node.begin()); + } return {}; } @@ -357,42 +393,51 @@ class AffectationToTupleProcessor final : public AffectationToDataVariantProcess { DataVariant value = m_rhs_node.execute(exec_policy); - std::visit( - [&](auto&& v) { - using T = std::decay_t<decltype(v)>; - if constexpr (std::is_same_v<T, ValueT>) { - *m_lhs = std::vector{std::move(v)}; - } else if constexpr (std::is_arithmetic_v<ValueT> and std::is_convertible_v<T, ValueT>) { - *m_lhs = std::vector{std::move(static_cast<ValueT>(v))}; - } else if constexpr (std::is_same_v<std::string, ValueT>) { - if constexpr (std::is_arithmetic_v<T>) { - *m_lhs = std::vector{std::move(std::to_string(v))}; - } else { - std::ostringstream os; - os << v; - *m_lhs = std::vector<std::string>{os.str()}; - } - } else if constexpr (is_tiny_vector_v<ValueT> or is_tiny_matrix_v<ValueT>) { - if constexpr (std::is_same_v<ValueT, TinyVector<1>> and std::is_arithmetic_v<T>) { - *m_lhs = std::vector<TinyVector<1>>{TinyVector<1>{static_cast<double>(v)}}; - } else if constexpr (std::is_same_v<ValueT, TinyMatrix<1>> and std::is_arithmetic_v<T>) { - *m_lhs = std::vector<TinyMatrix<1>>{TinyMatrix<1>{static_cast<double>(v)}}; - } else if constexpr (std::is_same_v<T, int64_t>) { - Assert(v == 0); - *m_lhs = std::vector<ValueT>{ValueT{zero}}; + try { + std::visit( + [&](auto&& v) { + using T = std::decay_t<decltype(v)>; + if constexpr (std::is_same_v<T, ValueT>) { + *m_lhs = std::vector{std::move(v)}; + } else if constexpr (std::is_arithmetic_v<ValueT> and std::is_convertible_v<T, ValueT>) { + if constexpr (std::is_same_v<uint64_t, ValueT> and std::is_same_v<int64_t, T>) { + if (v < 0) { + throw std::domain_error("trying to affect negative value (" + std::to_string(v) + ")"); + } + } + *m_lhs = std::vector{std::move(static_cast<ValueT>(v))}; + } else if constexpr (std::is_same_v<std::string, ValueT>) { + if constexpr (std::is_arithmetic_v<T>) { + *m_lhs = std::vector{std::move(std::to_string(v))}; + } else { + std::ostringstream os; + os << v; + *m_lhs = std::vector<std::string>{os.str()}; + } + } else if constexpr (is_tiny_vector_v<ValueT> or is_tiny_matrix_v<ValueT>) { + if constexpr (std::is_same_v<ValueT, TinyVector<1>> and std::is_arithmetic_v<T>) { + *m_lhs = std::vector<TinyVector<1>>{TinyVector<1>{static_cast<double>(v)}}; + } else if constexpr (std::is_same_v<ValueT, TinyMatrix<1>> and std::is_arithmetic_v<T>) { + *m_lhs = std::vector<TinyMatrix<1>>{TinyMatrix<1>{static_cast<double>(v)}}; + } else if constexpr (std::is_same_v<T, int64_t>) { + Assert(v == 0); + *m_lhs = std::vector<ValueT>{ValueT{zero}}; + } else { + // LCOV_EXCL_START + throw ParseError("unexpected error: unexpected right hand side type in affectation", m_rhs_node.begin()); + // LCOV_EXCL_STOP + } } else { // LCOV_EXCL_START throw ParseError("unexpected error: unexpected right hand side type in affectation", m_rhs_node.begin()); // LCOV_EXCL_STOP } - } else { - // LCOV_EXCL_START - throw ParseError("unexpected error: unexpected right hand side type in affectation", m_rhs_node.begin()); - // LCOV_EXCL_STOP - } - }, - value); - + }, + value); + } + catch (std::domain_error& e) { + throw ParseError(e.what(), m_rhs_node.begin()); + } return {}; } @@ -412,65 +457,37 @@ class AffectationToTupleFromListProcessor final : public AffectationToDataVarian { std::vector<ValueT> tuple_value(children_values.size()); for (size_t i = 0; i < children_values.size(); ++i) { - std::visit( - [&](auto&& child_value) { - using T = std::decay_t<decltype(child_value)>; - if constexpr (std::is_same_v<T, ValueT>) { - tuple_value[i] = child_value; - } else if constexpr (std::is_arithmetic_v<ValueT> and std::is_convertible_v<T, ValueT>) { - tuple_value[i] = static_cast<ValueT>(child_value); - } else if constexpr (std::is_same_v<std::string, ValueT>) { - if constexpr (std::is_arithmetic_v<T>) { - tuple_value[i] = std::to_string(child_value); - } else { - std::ostringstream os; - os << child_value; - tuple_value[i] = os.str(); - } - } else if constexpr (is_tiny_vector_v<ValueT>) { - if constexpr (std::is_same_v<T, AggregateDataVariant>) { - ValueT& v = tuple_value[i]; - Assert(ValueT::Dimension == child_value.size()); - for (size_t j = 0; j < ValueT::Dimension; ++j) { - std::visit( - [&](auto&& vj) { - using Ti = std::decay_t<decltype(vj)>; - if constexpr (std::is_convertible_v<Ti, typename ValueT::data_type>) { - v[j] = vj; - } else { - // LCOV_EXCL_START - throw ParseError("unexpected error: unexpected right hand side type in affectation", - m_rhs_node.children[i]->begin()); - // LCOV_EXCL_STOP - } - }, - child_value[j]); + try { + std::visit( + [&](auto&& child_value) { + using T = std::decay_t<decltype(child_value)>; + if constexpr (std::is_same_v<T, ValueT>) { + tuple_value[i] = child_value; + } else if constexpr (std::is_arithmetic_v<ValueT> and std::is_convertible_v<T, ValueT>) { + if constexpr (std::is_same_v<uint64_t, ValueT> and std::is_same_v<int64_t, T>) { + if (child_value < 0) { + throw std::domain_error("trying to affect negative value (" + std::to_string(child_value) + ")"); + } } - } else if constexpr (std::is_arithmetic_v<T>) { - if constexpr (std::is_same_v<ValueT, TinyVector<1>>) { - tuple_value[i][0] = child_value; + tuple_value[i] = static_cast<ValueT>(child_value); + } else if constexpr (std::is_same_v<std::string, ValueT>) { + if constexpr (std::is_arithmetic_v<T>) { + tuple_value[i] = std::to_string(child_value); } else { - // in this case a 0 is given - Assert(child_value == 0); - tuple_value[i] = ZeroType{}; + std::ostringstream os; + os << child_value; + tuple_value[i] = os.str(); } - } else { - // LCOV_EXCL_START - throw ParseError("unexpected error: unexpected right hand side type in affectation", - m_rhs_node.children[i]->begin()); - // LCOV_EXCL_STOP - } - } else if constexpr (is_tiny_matrix_v<ValueT>) { - if constexpr (std::is_same_v<T, AggregateDataVariant>) { - ValueT& A = tuple_value[i]; - Assert(A.numberOfRows() * A.numberOfColumns() == child_value.size()); - for (size_t j = 0, l = 0; j < A.numberOfRows(); ++j) { - for (size_t k = 0; k < A.numberOfColumns(); ++k, ++l) { + } else if constexpr (is_tiny_vector_v<ValueT>) { + if constexpr (std::is_same_v<T, AggregateDataVariant>) { + ValueT& v = tuple_value[i]; + Assert(ValueT::Dimension == child_value.size()); + for (size_t j = 0; j < ValueT::Dimension; ++j) { std::visit( - [&](auto&& Ajk) { - using Ti = std::decay_t<decltype(Ajk)>; + [&](auto&& vj) { + using Ti = std::decay_t<decltype(vj)>; if constexpr (std::is_convertible_v<Ti, typename ValueT::data_type>) { - A(j, k) = Ajk; + v[j] = vj; } else { // LCOV_EXCL_START throw ParseError("unexpected error: unexpected right hand side type in affectation", @@ -478,16 +495,56 @@ class AffectationToTupleFromListProcessor final : public AffectationToDataVarian // LCOV_EXCL_STOP } }, - child_value[l]); + child_value[j]); + } + } else if constexpr (std::is_arithmetic_v<T>) { + if constexpr (std::is_same_v<ValueT, TinyVector<1>>) { + tuple_value[i][0] = child_value; + } else { + // in this case a 0 is given + Assert(child_value == 0); + tuple_value[i] = ZeroType{}; } + } else { + // LCOV_EXCL_START + throw ParseError("unexpected error: unexpected right hand side type in affectation", + m_rhs_node.children[i]->begin()); + // LCOV_EXCL_STOP } - } else if constexpr (std::is_arithmetic_v<T>) { - if constexpr (std::is_same_v<ValueT, TinyMatrix<1>>) { - tuple_value[i](0, 0) = child_value; + } else if constexpr (is_tiny_matrix_v<ValueT>) { + if constexpr (std::is_same_v<T, AggregateDataVariant>) { + ValueT& A = tuple_value[i]; + Assert(A.numberOfRows() * A.numberOfColumns() == child_value.size()); + for (size_t j = 0, l = 0; j < A.numberOfRows(); ++j) { + for (size_t k = 0; k < A.numberOfColumns(); ++k, ++l) { + std::visit( + [&](auto&& Ajk) { + using Ti = std::decay_t<decltype(Ajk)>; + if constexpr (std::is_convertible_v<Ti, typename ValueT::data_type>) { + A(j, k) = Ajk; + } else { + // LCOV_EXCL_START + throw ParseError("unexpected error: unexpected right hand side type in affectation", + m_rhs_node.children[i]->begin()); + // LCOV_EXCL_STOP + } + }, + child_value[l]); + } + } + } else if constexpr (std::is_arithmetic_v<T>) { + if constexpr (std::is_same_v<ValueT, TinyMatrix<1>>) { + tuple_value[i](0, 0) = child_value; + } else { + // in this case a 0 is given + Assert(child_value == 0); + tuple_value[i] = ZeroType{}; + } } else { - // in this case a 0 is given - Assert(child_value == 0); - tuple_value[i] = ZeroType{}; + // LCOV_EXCL_START + throw ParseError("unexpected error: unexpected right hand side type in affectation", + m_rhs_node.children[i]->begin()); + // LCOV_EXCL_STOP } } else { // LCOV_EXCL_START @@ -495,14 +552,12 @@ class AffectationToTupleFromListProcessor final : public AffectationToDataVarian m_rhs_node.children[i]->begin()); // LCOV_EXCL_STOP } - } else { - // LCOV_EXCL_START - throw ParseError("unexpected error: unexpected right hand side type in affectation", - m_rhs_node.children[i]->begin()); - // LCOV_EXCL_STOP - } - }, - children_values[i]); + }, + children_values[i]); + } + catch (std::domain_error& e) { + throw ParseError(e.what(), m_rhs_node.children[i]->begin()); + } } *m_lhs = std::move(tuple_value); } @@ -620,11 +675,14 @@ class ListAffectationProcessor final : public INodeProcessor { AggregateDataVariant children_values = std::get<AggregateDataVariant>(m_node.children[1]->execute(exec_policy)); Assert(m_affectation_executor_list.size() == children_values.size()); - for (size_t i = 0; i < m_affectation_executor_list.size(); ++i) { - m_affectation_executor_list[i]->affect(exec_policy, std::move(children_values[i])); + try { + m_affectation_executor_list[i]->affect(exec_policy, std::move(children_values[i])); + } + catch (std::domain_error& e) { + throw ParseError(e.what(), m_node.children[1]->children[i]->begin()); + } } - return {}; } diff --git a/tests/test_AffectationProcessor.cpp b/tests/test_AffectationProcessor.cpp index dc32445565c970b8fb50022ea7bd9a3a5ec2c264..7995b71b0550de9b23bfadf51d9112e40e09df4a 100644 --- a/tests/test_AffectationProcessor.cpp +++ b/tests/test_AffectationProcessor.cpp @@ -68,6 +68,25 @@ REQUIRE_THROWS_WITH(ASTBuilder::build(input), error_message); \ } +#define CHECK_AFFECTATION_EXEC_THROWS_WITH(data, error_message) \ + { \ + TAO_PEGTL_NAMESPACE::string_input input{data, "test.pgs"}; \ + auto ast = ASTBuilder::build(input); \ + \ + ASTModulesImporter{*ast}; \ + \ + ASTSymbolTableBuilder{*ast}; \ + ASTNodeDataTypeBuilder{*ast}; \ + \ + ASTNodeDeclarationToAffectationConverter{*ast}; \ + ASTNodeTypeCleaner<language::var_declaration>{*ast}; \ + \ + ASTNodeExpressionBuilder{*ast}; \ + ExecutionPolicy exec_policy; \ + \ + REQUIRE_THROWS_WITH(ast->execute(exec_policy), error_message); \ + } + // clazy:excludeall=non-pod-global-static TEST_CASE("AffectationProcessor", "[language]") @@ -370,19 +389,26 @@ TEST_CASE("AffectationProcessor", "[language]") { CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2.3;", "undefined affectation type: N = R"); CHECK_AFFECTATION_THROWS_WITH("let n : N, n = \"bar\";", "undefined affectation type: N = string"); + CHECK_AFFECTATION_EXEC_THROWS_WITH("let n : N, n = -2;", "trying to affect negative value (-2)"); CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n += 1.1;", "undefined affectation type: N += R"); CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n += \"foo\";", "undefined affectation type: N += string"); + CHECK_AFFECTATION_EXEC_THROWS_WITH("let n : N, n = 2; n+=-3;", + "trying to affect negative value (lhs: 2 rhs: -3)"); CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n -= 1.1;", "undefined affectation type: N -= R"); CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n -= \"bar\";", "undefined affectation type: N -= string"); + CHECK_AFFECTATION_EXEC_THROWS_WITH("let n : N, n = 2; n-=3;", + "trying to affect negative value (lhs: 2 rhs: 3)"); CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n *= 2.51;", "undefined affectation type: N *= R"); CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n *= \"foobar\";", "undefined affectation type: N *= string"); + CHECK_AFFECTATION_EXEC_THROWS_WITH("let n : N, n = 2; n*= -2;", "trying to affect negative value (-2)"); CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n /= true;", "undefined affectation type: N /= B"); CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n /= 2.51;", "undefined affectation type: N /= R"); CHECK_AFFECTATION_THROWS_WITH("let n : N, n = 2; n /= \"foo\";", "undefined affectation type: N /= string"); + CHECK_AFFECTATION_EXEC_THROWS_WITH("let n : N, n = 2; n/= -2;", "trying to affect negative value (-2)"); } SECTION("-> Z")