:(scenario can_modify_value_ingredients)
% Hide_warnings = true;
recipe main [
local-scope
p:address:shared:point <- new point:type
foo *p
]
recipe foo p:point [
local-scope
load-ingredients
x:address:number <- get-address p, x:offset
*x <- copy 34
]
$warn: 0
:(scenario can_modify_ingredients_that_are_also_products)
% Hide_warnings = true;
recipe main [
local-scope
p:address:shared:point <- new point:type
p <- foo p
]
recipe foo p:address:shared:point -> p:address:shared:point [
local-scope
load-ingredients
x:address:number <- get-address *p, x:offset
*x <- copy 34
]
$warn: 0
:(scenario ignore_literal_ingredients_for_immutability_checks)
% Hide_warnings = true;
recipe main [
local-scope
p:address:shared:d1 <- new d1:type
q:number <- foo p
]
recipe foo p:address:shared:d1 -> q:number [
local-scope
load-ingredients
x:address:shared:d1 <- new d1:type
y:address:number <- get-address *x, p:offset
q <- copy 34
]
container d1 [
p:number
q:number
]
$warn: 0
:(scenario cannot_take_address_inside_immutable_ingredients)
% Hide_warnings = true;
recipe main [
local-scope
p:address:shared:point <- new point:type
foo p
]
recipe foo p:address:shared:point [
local-scope
load-ingredients
x:address:number <- get-address *p, x:offset
*x <- copy 34
]
+warn: foo: cannot modify ingredient p after instruction 'x:address:number <- get-address *p, x:offset' because it's not also a product of foo
:(scenario cannot_call_mutating_recipes_on_immutable_ingredients)
% Hide_warnings = true;
recipe main [
local-scope
p:address:shared:point <- new point:type
foo p
]
recipe foo p:address:shared:point [
local-scope
load-ingredients
bar p
]
recipe bar p:address:shared:point -> p:address:shared:point [
local-scope
load-ingredients
x:address:number <- get-address *p, x:offset
*x <- copy 34
]
+warn: foo: cannot modify ingredient p at instruction 'bar p' because it's not also a product of foo
:(scenario cannot_modify_copies_of_immutable_ingredients)
% Hide_warnings = true;
recipe main [
local-scope
p:address:shared:point <- new point:type
foo p
]
recipe foo p:address:shared:point [
local-scope
load-ingredients
q:address:shared:point <- copy p
x:address:number <- get-address *q, x:offset
]
+warn: foo: cannot modify q after instruction 'x:address:number <- get-address *q, x:offset' because that would modify ingredient p which is not also a product of foo
:(scenario can_traverse_immutable_ingredients)
% Hide_warnings = true;
container test-list [
next:address:shared:test-list
]
recipe main [
local-scope
p:address:shared:test-list <- new test-list:type
foo p
]
recipe foo p:address:shared:test-list [
local-scope
load-ingredients
p2:address:shared:test-list <- bar p
]
recipe bar x:address:shared:test-list -> y:address:shared:test-list [
local-scope
load-ingredients
y <- get *x, next:offset
]
$warn: 0
:(scenario handle_optional_ingredients_in_immutability_checks)
% Hide_warnings = true;
recipe main [
k:address:shared:number <- new number:type
test k
]
recipe test k:address:shared:number [
local-scope
load-ingredients
foo k
]
recipe foo -> [
local-scope
load-ingredients
k:address:shared:number, found?:boolean <- next-ingredient
]
$warn: 0
:(before "End Transforms")
Transform.push_back(check_immutable_ingredients);
:(code)
void check_immutable_ingredients(recipe_ordinal r) {
const recipe& caller = get(Recipe, r);
if (!caller.has_header) return;
for (long long int i = 0; i < SIZE(caller.ingredients); ++i) {
const reagent& current_ingredient = caller.ingredients.at(i);
if (!is_mu_address(current_ingredient)) continue;
if (is_present_in_products(caller, current_ingredient.name)) continue;
set<string> immutable_vars;
immutable_vars.insert(current_ingredient.name);
for (long long int i = 0; i < SIZE(caller.steps); ++i) {
const instruction& inst = caller.steps.at(i);
check_immutable_ingredient_in_instruction(inst, immutable_vars, current_ingredient.name, caller);
update_aliases(inst, immutable_vars);
}
}
}
void update_aliases(const instruction& inst, set<string>& current_ingredient_and_aliases) {
set<long long int> current_ingredient_indices = ingredient_indices(inst, current_ingredient_and_aliases);
if (!contains_key(Recipe, inst.operation)) {
if (inst.operation == COPY) {
for (set<long long int>::iterator p = current_ingredient_indices.begin(); p != current_ingredient_indices.end(); ++p) {
current_ingredient_and_aliases.insert(inst.products.at(*p).name);
}
}
}
else {
set<long long int> contained_in_product_indices = scan_contained_in_product_indices(inst, current_ingredient_indices);
for (set<long long int>::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<long long int> scan_contained_in_product_indices(const instruction& inst, set<long long int>& ingredient_indices) {
set<string> selected_ingredient_names;
const recipe& callee = get(Recipe, inst.operation);
for (set<long long int>::iterator p = ingredient_indices.begin(); p != ingredient_indices.end(); ++p) {
if (*p >= SIZE(callee.ingredients)) continue;
selected_ingredient_names.insert(callee.ingredients.at(*p).name);
}
set<long long int> 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;<