//: Ingredients of a recipe are meant to be immutable unless they're also //: products. This layer will start enforcing this check. //: //: One hole for now: variables in surrounding spaces are implicitly mutable. :(scenario can_modify_ingredients_that_are_also_products) # mutable container def main [ local-scope p:point <- merge 34, 35 p <- foo p ] def foo p:point -> p:point [ local-scope load-ingredients p <- put p, x:offset, 34 ] $error: 0 :(scenario can_modify_ingredients_that_are_also_products_2) def main [ local-scope p:address:point <- new point:type p <- foo p ] # mutable address to container def foo p:address:point -> p:address:point [ local-scope load-ingredients *p <- put *p, x:offset, 34 ] $error: 0 :(scenario can_modify_ingredients_that_are_also_products_3) def main [ local-scope p:address:array:number <- new number:type, 3 p <- foo p ] # mutable address def foo p:address:array:number -> p:address:array:number [ local-scope load-ingredients *p <- put-index *p, 0, 34 ] $error: 0 :(scenario ignore_literal_ingredients_for_immutability_checks) def main [ local-scope p:address:d1 <- new d1:type q:number <- foo p ] def foo p:address:d1 -> q:number [ local-scope load-ingredients x:address:d1 <- new d1:type *x <- put *x, p:offset, 34 # ignore this 'p' reply 36 ] container d1 [ p:number q:number ] $error: 0 :(scenario cannot_modify_immutable_ingredients) % Hide_errors = true; def main [ local-scope x:address:number <- new number:type foo x ] # immutable address to primitive def foo x:address:number [ local-scope load-ingredients *x <- copy 34 ] +error: foo: cannot modify 'x' in instruction '*x <- copy 34' because it's an ingredient of recipe foo but not also a product :(scenario cannot_modify_immutable_containers) % Hide_errors = true; def main [ local-scope x:point-number <- merge 34, 35, 36 foo x ] # immutable container def foo x:point-number [ local-scope load-ingredients # copy an element: ok y:point <- get x, xy:offset # modify the element: boom # This could be ok if y contains no addresses, but we're not going to try to be that smart. # It also makes the rules easier to reason about. If it's just an ingredient, just don't try to change it. y <- put y, x:offset, 37 ] +error: foo: cannot modify 'y' in instruction 'y <- put y, x:offset, 37' because that would modify 'x' which is an ingredient of recipe foo but not also a product :(scenario can_modify_immutable_pointers) def main [ local-scope x:address:number <- new number:type foo x ] def foo x:address:number [ local-scope load-ingredients # modify the address, not the payload x <- copy 0 ] $error: 0 :(scenario can_modify_immutable_pointers_but_not_their_payloads) % Hide_errors = true; def main [ local-scope x:address:number <- new number:type foo x ] def foo x:address:number [ local-scope load-ingredients # modify address; ok x <- new number:type # modify payload: boom # this could be ok, but we're not going to try to be that smart *x <- copy 34 ] +error: foo: cannot modify 'x' in instruction '*x <- copy 34' because it's an ingredient of recipe foo but not also a product :(scenario cannot_call_mutating_recipes_on_immutable_ingredients) % Hide_errors = true; def main [ local-scope p:address:point <- new point:type foo p ] def foo p:address:point [ local-scope load-ingredients bar p ] def bar p:address:point -> p:address:point [ local-scope load-ingredients # p could be modified here, but it doesn't have to be, it's already marked # mutable in the header ] +error: foo: cannot modify 'p' in instruction 'bar p' because it's an ingredient of recipe foo but not also a product :(scenario cannot_modify_copies_of_immutable_ingredients) % Hide_errors = true; def main [ local-scope p:address:point <- new point:type foo p ] def foo p:address:point [ local-scope load-ingredients q:address:point <- copy p *q <- put *q, x:offset, 34 ] +error: foo: cannot modify 'q' in instruction '*q <- put *q, x:offset, 34' because that would modify p which is an ingredient of recipe foo but not also a product :(scenario can_modify_copies_of_mutable_ingredients) def main [ local-scope p:address:point <- new point:type foo p ] def foo p:address:point -> p:address:point [ local-scope load-ingredients q:address:point <- copy p *q <- put *q, x:offset, 34 ] $error: 0 :(scenario cannot_modify_address_inside_immutable_ingredients) % Hide_errors = true; container foo [ x:address:array:number # contains an address ] def main [ # don't run anything ] def foo a:address:foo [ local-scope load-ingredients x:address:array:number <- get *a, x:offset # just a regular get of the container *x <- put-index *x, 0, 34 # but then a put-index on the result ] +error: foo: cannot modify 'x' in instruction '*x <- put-index *x, 0, 34' because that would modify a which is an ingredient of recipe foo but not also a product :(scenario cannot_modify_address_inside_immutable_ingredients_2) container foo [ x:address:array:number # contains an address ] def main [ # don't run anything ] def foo a:address:foo [ local-scope load-ingredients b:foo <- merge 0 # modify b, completely unrelated to immutable ingredient a x:address:array:number <- get b, x:offset *x <- put-index *x, 0, 34 ] $error: 0 :(scenario cannot_modify_address_inside_immutable_ingredients_3) % Hide_errors = true; c
//: allow using literal strings anywhere that will accept immutable strings
:(scenario passing_literals_to_recipes)
def main [
1:number/raw <- foo [abc]
]
def foo x:address:array:character -> n:number [
local-scope
load-ingredients
n <- length *x
]
+mem: storing 3 in location 1
:(before "End Instruction Inserting/Deleting Transforms")
initialize_transform_rewrite_literal_string_to_text();
Transform.push_back(rewrite_literal_string_to_text);
:(before "End Globals")
set<string> recipes_taking_literal_strings;
:(code)
void initialize_transform_rewrite_literal_string_to_text() {
recipes_taking_literal_strings.insert("$print");
recipes_taking_literal_strings.insert("$dump-trace");
recipes_taking_literal_strings.insert("$system");
recipes_taking_literal_strings.insert("trace");
recipes_taking_literal_strings.insert("stash");
recipes_taking_literal_strings.insert("assert");
recipes_taking_literal_strings.insert("new");
recipes_taking_literal_strings.insert("run");
recipes_taking_literal_strings.insert("memory-should-contain");
recipes_taking_literal_strings.insert("trace-should-contain");
recipes_taking_literal_strings.insert("trace-should-not-contain");
recipes_taking_literal_strings.insert("check-trace-count-for-label");
// End initialize_transform_rewrite_literal_string_to_text()
}
void rewrite_literal_string_to_text(recipe_ordinal r) {
recipe& caller = get(Recipe, r);
trace(9991, "transform") << "--- rewrite literal strings in recipe " << caller.name << end();
if (contains_numeric_locations(caller)) return;
vector<instruction> new_instructions;
for (int i = 0;