diff --git a/editor/CMakeLists.txt b/editor/CMakeLists.txt index a88e8e8..8c0462c 100644 --- a/editor/CMakeLists.txt +++ b/editor/CMakeLists.txt @@ -142,6 +142,9 @@ target_include_directories(step41_test PRIVATE src) add_executable(step42_test tests/step42_test.cpp) target_include_directories(step42_test PRIVATE src) +add_executable(step43_test tests/step43_test.cpp) +target_include_directories(step43_test PRIVATE src) + add_executable(whetstone_editor src/main.cpp) target_include_directories(whetstone_editor PRIVATE src) # find_package(SDL2 REQUIRED) # Commented out for now to avoid build issues diff --git a/editor/tests/step43_test.cpp b/editor/tests/step43_test.cpp new file mode 100644 index 0000000..4e71a24 --- /dev/null +++ b/editor/tests/step43_test.cpp @@ -0,0 +1,225 @@ +// Step 43 TDD Test: Memory strategy code generation +// +// Tests the refactoring from DerefStrategy to canonical annotation types +// and their effect on C++ code generation: +// 1. DeallocateAnnotation("Explicit") → raw pointers with new/delete +// 2. LifetimeAnnotation("RAII") → std::unique_ptr, RAII destructors +// 3. ReclaimAnnotation("Tracing") → std::shared_ptr for GC-like ref counting +// 4. OwnerAnnotation("Single") → std::unique_ptr with strict ownership +// 5. OwnerAnnotation("Shared_ARC") → std::shared_ptr with ARC semantics +// 6. New annotation classes exist and inherit from Annotation +// 7. DerefStrategy still works as deprecated wrapper +// +// Will fail to compile until canonical annotation classes are created in Annotation.h. + +#include +#include +#include +#include "ast/Generator.h" + +static bool contains(const std::string& haystack, const std::string& needle) { + return haystack.find(needle) != std::string::npos; +} + +int main() { + int passed = 0; + int failed = 0; + + // --- Test 1: DeallocateAnnotation exists and inherits from Annotation --- + { + DeallocateAnnotation anno; + anno.strategy = "Explicit"; + Annotation* base = &anno; + (void)base; + assert(anno.strategy == "Explicit" && "Should store strategy"); + + std::cout << "Test 1 PASS: DeallocateAnnotation exists with strategy field" << std::endl; + ++passed; + } + + // --- Test 2: LifetimeAnnotation exists --- + { + LifetimeAnnotation anno; + anno.strategy = "RAII"; + Annotation* base = &anno; + (void)base; + assert(anno.strategy == "RAII" && "Should store strategy"); + + std::cout << "Test 2 PASS: LifetimeAnnotation exists with strategy field" << std::endl; + ++passed; + } + + // --- Test 3: ReclaimAnnotation exists --- + { + ReclaimAnnotation anno; + anno.strategy = "Tracing"; + Annotation* base = &anno; + (void)base; + assert(anno.strategy == "Tracing" && "Should store strategy"); + + std::cout << "Test 3 PASS: ReclaimAnnotation exists with strategy field" << std::endl; + ++passed; + } + + // --- Test 4: OwnerAnnotation exists --- + { + OwnerAnnotation anno; + anno.strategy = "Single"; + Annotation* base = &anno; + (void)base; + assert(anno.strategy == "Single" && "Should store strategy"); + + std::cout << "Test 4 PASS: OwnerAnnotation exists with strategy field" << std::endl; + ++passed; + } + + // --- Test 5: AllocateAnnotation exists --- + { + AllocateAnnotation anno; + anno.strategy = "Static"; + Annotation* base = &anno; + (void)base; + assert(anno.strategy == "Static" && "Should store strategy"); + + std::cout << "Test 5 PASS: AllocateAnnotation exists with strategy field" << std::endl; + ++passed; + } + + // --- Test 6: @Deallocate(Explicit) → raw pointer in C++ --- + { + // Build a function with DeallocateAnnotation + Function fn("f1", "allocateWidget"); + PrimitiveType* retType = new PrimitiveType("t1", "int"); + fn.setChild("returnType", retType); + + DeallocateAnnotation* anno = new DeallocateAnnotation(); + anno->strategy = "Explicit"; + fn.addChild("annotations", anno); + + // Add a variable declaration in body + Variable* var = new Variable("v1", "widget"); + CustomType* widgetType = new CustomType("t2", "Widget"); + var->setChild("type", widgetType); + fn.addChild("body", var); + + CppGenerator gen; + std::string output = gen.generate(&fn); + + // With @Deallocate(Explicit), should use raw pointers + assert(contains(output, "Widget") && "Should contain the type name"); + assert(contains(output, "allocateWidget") && "Should contain function name"); + // Should produce raw pointer syntax (Widget* or new Widget) + assert((contains(output, "*") || contains(output, "new")) && + "@Deallocate(Explicit) should produce raw pointer or new operator"); + + std::cout << "Test 6 PASS: @Deallocate(Explicit) produces raw pointer code" << std::endl; + ++passed; + + delete widgetType; + delete var; + delete anno; + delete retType; + } + + // --- Test 7: @Lifetime(RAII) → unique_ptr in C++ --- + { + Function fn("f1", "createWidget"); + PrimitiveType* retType = new PrimitiveType("t1", "int"); + fn.setChild("returnType", retType); + + LifetimeAnnotation* anno = new LifetimeAnnotation(); + anno->strategy = "RAII"; + fn.addChild("annotations", anno); + + Variable* var = new Variable("v1", "widget"); + CustomType* widgetType = new CustomType("t2", "Widget"); + var->setChild("type", widgetType); + fn.addChild("body", var); + + CppGenerator gen; + std::string output = gen.generate(&fn); + + assert(contains(output, "unique_ptr") && "@Lifetime(RAII) should produce unique_ptr"); + + std::cout << "Test 7 PASS: @Lifetime(RAII) produces unique_ptr code" << std::endl; + ++passed; + + delete widgetType; + delete var; + delete anno; + delete retType; + } + + // --- Test 8: @Reclaim(Tracing) → shared_ptr in C++ --- + { + Function fn("f1", "getWidget"); + PrimitiveType* retType = new PrimitiveType("t1", "int"); + fn.setChild("returnType", retType); + + ReclaimAnnotation* anno = new ReclaimAnnotation(); + anno->strategy = "Tracing"; + fn.addChild("annotations", anno); + + Variable* var = new Variable("v1", "widget"); + CustomType* widgetType = new CustomType("t2", "Widget"); + var->setChild("type", widgetType); + fn.addChild("body", var); + + CppGenerator gen; + std::string output = gen.generate(&fn); + + assert(contains(output, "shared_ptr") && "@Reclaim(Tracing) should produce shared_ptr"); + + std::cout << "Test 8 PASS: @Reclaim(Tracing) produces shared_ptr code" << std::endl; + ++passed; + + delete widgetType; + delete var; + delete anno; + delete retType; + } + + // --- Test 9: @Owner(Shared_ARC) → shared_ptr in C++ --- + { + Function fn("f1", "shareWidget"); + PrimitiveType* retType = new PrimitiveType("t1", "int"); + fn.setChild("returnType", retType); + + OwnerAnnotation* anno = new OwnerAnnotation(); + anno->strategy = "Shared_ARC"; + fn.addChild("annotations", anno); + + Variable* var = new Variable("v1", "widget"); + CustomType* widgetType = new CustomType("t2", "Widget"); + var->setChild("type", widgetType); + fn.addChild("body", var); + + CppGenerator gen; + std::string output = gen.generate(&fn); + + assert(contains(output, "shared_ptr") && "@Owner(Shared_ARC) should produce shared_ptr"); + + std::cout << "Test 9 PASS: @Owner(Shared_ARC) produces shared_ptr code" << std::endl; + ++passed; + + delete widgetType; + delete var; + delete anno; + delete retType; + } + + // --- Test 10: Old DerefStrategy still works (backward compat) --- + { + DerefStrategy deref("d1", "imperative"); + Annotation* base = &deref; + (void)base; + assert(deref.strategy == "imperative" && "Old DerefStrategy should still work"); + + std::cout << "Test 10 PASS: DerefStrategy still functional (backward compat)" << std::endl; + ++passed; + } + + // --- Summary --- + std::cout << "\n=== Step 43 Results: " << passed << " passed, " << failed << " failed ===" << std::endl; + return failed > 0 ? 1 : 0; +}