From d332bec19d47bfb7a2337248a80a2b8b938a9b40 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Thu, 19 Nov 2015 15:54:27 -0800 Subject: infect immutability across recipes If a product is contained-in some ingredient, then the caller will check the product for mutations as well. For example, consider this linked-list operation: b:address:list:number <- next a:address:list:number If 'a' is immutable in the surrounding recipe, you probably want 'b' to be immutable as well. You can achieve this by giving 'next' the following header (ignoring shape-shifting): recipe next a:address:list:number -> b:address:list:number/contained-in:a This is the theory, anyway. Rather to my surprise, this doesn't trigger any issues with existing code. That's probably too good to be true. --- 060immutable.cc | 49 +++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 47 insertions(+), 2 deletions(-) diff --git a/060immutable.cc b/060immutable.cc index bbbcac96..8eaf8b98 100644 --- a/060immutable.cc +++ b/060immutable.cc @@ -129,10 +129,10 @@ void check_immutable_ingredients(recipe_ordinal r) { } void update_aliases(const instruction& inst, set& current_ingredient_and_aliases) { + set current_ingredient_indices = ingredient_indices(inst, current_ingredient_and_aliases); if (!contains_key(Recipe, inst.operation)) { // primitive recipe if (inst.operation == COPY) { - set current_ingredient_indices = ingredient_indices(inst, current_ingredient_and_aliases); for (set::iterator p = current_ingredient_indices.begin(); p != current_ingredient_indices.end(); ++p) { current_ingredient_and_aliases.insert(inst.products.at(*p).name); } @@ -140,9 +140,54 @@ void update_aliases(const instruction& inst, set& current_ingredient_and } else { // defined recipe + set contained_in_product_indices = scan_contained_in_product_indices(inst, current_ingredient_indices); + for (set::iterator p = contained_in_product_indices.begin(); p != contained_in_product_indices.end(); ++p) { + if (*p < SIZE(inst.products)) + current_ingredient_and_aliases.insert(inst.products.at(*p).name); + } } } +set scan_contained_in_product_indices(const instruction& inst, set& ingredient_indices) { + set selected_ingredient_names; + const recipe& callee = get(Recipe, inst.operation); + for (set::iterator p = ingredient_indices.begin(); p != ingredient_indices.end(); ++p) + selected_ingredient_names.insert(callee.ingredients.at(*p).name); + set result; + for (long long int i = 0; i < SIZE(callee.products); ++i) { + const reagent& current_product = callee.products.at(i); + const string_tree* contained_in_name = property(current_product, "contained-in"); + if (contained_in_name && selected_ingredient_names.find(contained_in_name->value) != selected_ingredient_names.end()) + result.insert(i); + } + return result; +} + +:(scenarios transform) +:(scenario immutability_infects_contained_in_variables) +% Hide_warnings = true; +container test-list [ + next:address:test-list +] +recipe main [ + local-scope + p:address:test-list <- new test-list:type + foo p +] +recipe foo p:address:test-list [ # p is immutable + local-scope + load-ingredients + p2:address:test-list <- test-next p # p2 is immutable + p3:address:address:test-list <- get-address *p2, next:offset # signal modification of p2 +] +recipe test-next x:address:test-list -> y:address:test-list/contained-in:x [ + local-scope + load-ingredients + y <- get *x, next:offset +] +$warn: 1 + +:(code) void check_immutable_ingredient_in_instruction(const instruction& inst, const set& current_ingredient_and_aliases, const recipe& caller) { set current_ingredient_indices = ingredient_indices(inst, current_ingredient_and_aliases); if (current_ingredient_indices.empty()) return; // ingredient not found in call @@ -212,7 +257,7 @@ set ingredient_indices(const instruction& inst, const set :(scenarios transform) :(scenario can_modify_contained_in_addresses) -#% Hide_warnings = true; +% Hide_warnings = true; container test-list [ next:address:test-list ] -- cgit 1.4.1-2-gfad0