From b808ab989f2bf6ac4ac28edeb638da9017e1b6b7 Mon Sep 17 00:00:00 2001 From: "Alastair F. Donaldson" Date: Thu, 18 Jun 2026 15:40:03 +0100 Subject: [PATCH] Account for duplication in template rewrites Fixes #364. --- src/libdredd/src/mutate_ast_consumer.cc | 5 + test/single_file/true_template_arg.cc | 16 ++++ .../single_file/true_template_arg.cc.expected | 92 ++++++++++++++++++ .../true_template_arg.cc.noopt.expected | 96 +++++++++++++++++++ 4 files changed, 209 insertions(+) create mode 100644 test/single_file/true_template_arg.cc create mode 100644 test/single_file/true_template_arg.cc.expected create mode 100644 test/single_file/true_template_arg.cc.noopt.expected diff --git a/src/libdredd/src/mutate_ast_consumer.cc b/src/libdredd/src/mutate_ast_consumer.cc index a396e299..568e5ea1 100644 --- a/src/libdredd/src/mutate_ast_consumer.cc +++ b/src/libdredd/src/mutate_ast_consumer.cc @@ -217,8 +217,13 @@ void MutateAstConsumer::RewriteExpressionsInMainFile() { } // Rewrite the constant integer arguments of builtin functions and templates. + std::unordered_set already_rewritten; for (const auto* constant_argument_expresion : visitor_->GetConstantArgumentsToRewrite()) { + if (already_rewritten.contains(constant_argument_expresion)) { + continue; + } + already_rewritten.insert(constant_argument_expresion); if (const auto integer_const_expr = constant_argument_expresion->getIntegerConstantExpr( compiler_instance_->getASTContext())) { diff --git a/test/single_file/true_template_arg.cc b/test/single_file/true_template_arg.cc new file mode 100644 index 00000000..b287280a --- /dev/null +++ b/test/single_file/true_template_arg.cc @@ -0,0 +1,16 @@ +template class C { +public: + C(int*) {} +}; + +using S = C; + +void f() { + S SC(0); +} + +bool g() { + S SC(0); + int x; + x = 0; +} diff --git a/test/single_file/true_template_arg.cc.expected b/test/single_file/true_template_arg.cc.expected new file mode 100644 index 00000000..ad7e7eeb --- /dev/null +++ b/test/single_file/true_template_arg.cc.expected @@ -0,0 +1,92 @@ +// DREDD PRELUDE START +// If this has been inserted at an inappropriate place in a source file, +// declare a placeholder function with the following signature to +// mandate where the prelude should be placed: +// +// void __dredd_prelude_start(); +// +#include +#include +#include +#include +#include +#include +#include + + +#ifdef _MSC_VER +#define thread_local __declspec(thread) +#elif __APPLE__ +#define thread_local __thread +#endif + +static thread_local bool __dredd_some_mutation_enabled = true; +static bool __dredd_enabled_mutation(int local_mutation_id) { + static thread_local bool initialized = false; + static thread_local uint64_t enabled_bitset[1]; + if (!initialized) { + bool some_mutation_enabled = false; + const char* dredd_environment_variable = std::getenv("DREDD_ENABLED_MUTATION"); + if (dredd_environment_variable != nullptr) { + std::string contents(dredd_environment_variable); + while (true) { + size_t pos = contents.find(","); + std::string token = (pos == std::string::npos ? contents : contents.substr(0, pos)); + if (!token.empty()) { + int value = std::stoi(token); + int local_value = value - 0; + if (local_value >= 0 && local_value < 13) { + enabled_bitset[local_value / 64] |= (static_cast(1) << (local_value % 64)); + some_mutation_enabled = true; + } + } + if (pos == std::string::npos) { + break; + } + contents.erase(0, pos + 1); + } + } + initialized = true; + __dredd_some_mutation_enabled = some_mutation_enabled; + } + return (enabled_bitset[local_mutation_id / 64] & (static_cast(1) << (local_mutation_id % 64))) != 0; +} + +static int& __dredd_replace_binary_operator_Assign_arg1_int_arg2_int(int& arg1, int arg2, int local_mutation_id) { + if (!__dredd_some_mutation_enabled) return arg1 = arg2; + if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 += arg2; + if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 &= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 2)) return arg1 /= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 3)) return arg1 *= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 4)) return arg1 |= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 5)) return arg1 %= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 6)) return arg1 <<= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 7)) return arg1 >>= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 8)) return arg1 -= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 9)) return arg1 ^= arg2; + return arg1 = arg2; +} + +static int __dredd_replace_expr_int_zero(int arg, int local_mutation_id) { + if (!__dredd_some_mutation_enabled) return arg; + if (__dredd_enabled_mutation(local_mutation_id + 0)) return 1; + if (__dredd_enabled_mutation(local_mutation_id + 1)) return -1; + return arg; +} + +template class C { +public: + C(int*) {} +}; + +using S = C<1>; + +void f() { + S SC(0); +} + +bool g() { + S SC(0); + int x; + if (!__dredd_enabled_mutation(12)) { __dredd_replace_binary_operator_Assign_arg1_int_arg2_int(x , __dredd_replace_expr_int_zero(0, 0), 2); } +} diff --git a/test/single_file/true_template_arg.cc.noopt.expected b/test/single_file/true_template_arg.cc.noopt.expected new file mode 100644 index 00000000..5e54aa81 --- /dev/null +++ b/test/single_file/true_template_arg.cc.noopt.expected @@ -0,0 +1,96 @@ +// DREDD PRELUDE START +// If this has been inserted at an inappropriate place in a source file, +// declare a placeholder function with the following signature to +// mandate where the prelude should be placed: +// +// void __dredd_prelude_start(); +// +#include +#include +#include +#include +#include +#include +#include + + +#ifdef _MSC_VER +#define thread_local __declspec(thread) +#elif __APPLE__ +#define thread_local __thread +#endif + +static thread_local bool __dredd_some_mutation_enabled = true; +static bool __dredd_enabled_mutation(int local_mutation_id) { + static thread_local bool initialized = false; + static thread_local uint64_t enabled_bitset[1]; + if (!initialized) { + bool some_mutation_enabled = false; + const char* dredd_environment_variable = std::getenv("DREDD_ENABLED_MUTATION"); + if (dredd_environment_variable != nullptr) { + std::string contents(dredd_environment_variable); + while (true) { + size_t pos = contents.find(","); + std::string token = (pos == std::string::npos ? contents : contents.substr(0, pos)); + if (!token.empty()) { + int value = std::stoi(token); + int local_value = value - 0; + if (local_value >= 0 && local_value < 17) { + enabled_bitset[local_value / 64] |= (static_cast(1) << (local_value % 64)); + some_mutation_enabled = true; + } + } + if (pos == std::string::npos) { + break; + } + contents.erase(0, pos + 1); + } + } + initialized = true; + __dredd_some_mutation_enabled = some_mutation_enabled; + } + return (enabled_bitset[local_mutation_id / 64] & (static_cast(1) << (local_mutation_id % 64))) != 0; +} + +static int& __dredd_replace_binary_operator_Assign_arg1_int_arg2_int(int& arg1, int arg2, int local_mutation_id) { + if (!__dredd_some_mutation_enabled) return arg1 = arg2; + if (__dredd_enabled_mutation(local_mutation_id + 0)) return arg1 += arg2; + if (__dredd_enabled_mutation(local_mutation_id + 1)) return arg1 &= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 2)) return arg1 /= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 3)) return arg1 *= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 4)) return arg1 |= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 5)) return arg1 %= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 6)) return arg1 <<= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 7)) return arg1 >>= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 8)) return arg1 -= arg2; + if (__dredd_enabled_mutation(local_mutation_id + 9)) return arg1 ^= arg2; + return arg1 = arg2; +} + +static int __dredd_replace_expr_int(int arg, int local_mutation_id) { + if (!__dredd_some_mutation_enabled) return arg; + if (__dredd_enabled_mutation(local_mutation_id + 0)) return !(arg); + if (__dredd_enabled_mutation(local_mutation_id + 1)) return ~(arg); + if (__dredd_enabled_mutation(local_mutation_id + 2)) return -(arg); + if (__dredd_enabled_mutation(local_mutation_id + 3)) return 0; + if (__dredd_enabled_mutation(local_mutation_id + 4)) return 1; + if (__dredd_enabled_mutation(local_mutation_id + 5)) return -1; + return arg; +} + +template class C { +public: + C(int*) {} +}; + +using S = C<1>; + +void f() { + S SC(0); +} + +bool g() { + S SC(0); + int x; + if (!__dredd_enabled_mutation(16)) { __dredd_replace_binary_operator_Assign_arg1_int_arg2_int(x , __dredd_replace_expr_int(0, 0), 6); } +}