diff options
author | Kartik K. Agaram <vc@akkartik.com> | 2016-05-20 12:49:31 -0700 |
---|---|---|
committer | Kartik K. Agaram <vc@akkartik.com> | 2016-05-20 12:49:31 -0700 |
commit | be92feb7611687539d62e53407273d1ef2ade76e (patch) | |
tree | dbc094a32a579ab79b05f7e454ef182d2d326e8f /054shape_shifting_recipe.cc | |
parent | 7890b8cef2c330ad69e2ce5861931ff2768d6194 (diff) | |
download | mu-be92feb7611687539d62e53407273d1ef2ade76e.tar.gz |
2987
Diffstat (limited to '054shape_shifting_recipe.cc')
-rw-r--r-- | 054shape_shifting_recipe.cc | 1073 |
1 files changed, 0 insertions, 1073 deletions
diff --git a/054shape_shifting_recipe.cc b/054shape_shifting_recipe.cc deleted file mode 100644 index c69678bf..00000000 --- a/054shape_shifting_recipe.cc +++ /dev/null @@ -1,1073 +0,0 @@ -//:: Like container definitions, recipes too can contain type parameters. - -:(scenario shape_shifting_recipe) -def main [ - 10:point <- merge 14, 15 - 11:point <- foo 10:point -] -# non-matching variant -def foo a:number -> result:number [ - local-scope - load-ingredients - result <- copy 34 -] -# matching shape-shifting variant -def foo a:_t -> result:_t [ - local-scope - load-ingredients - result <- copy a -] -+mem: storing 14 in location 11 -+mem: storing 15 in location 12 - -//: Before anything else, disable transforms for shape-shifting recipes and -//: make sure we never try to actually run a shape-shifting recipe. We should -//: be rewriting such instructions to *specializations* with the type -//: ingredients filled in. - -:(before "End Transform Checks") -if (any_type_ingredient_in_header(/*recipe_ordinal*/p->first)) continue; - -:(after "Running One Instruction") -if (Current_routine->calls.front().running_step_index == 0 - && any_type_ingredient_in_header(Current_routine->calls.front().running_recipe)) { -//? DUMP(""); - raise << "ran into unspecialized shape-shifting recipe " << current_recipe_name() << '\n' << end(); -//? exit(0); -} - -//: Make sure we don't match up literals with type ingredients without -//: specialization. -:(before "End Matching Types For Literal(to)") -if (contains_type_ingredient_name(to)) return false; - -//: save original name of specialized recipes -:(before "End recipe Fields") -string original_name; -//: original name is only set during load -:(before "End Load Recipe Name") -result.original_name = result.name; - -:(after "Static Dispatch Phase 3") -candidates = strictly_matching_shape_shifting_variants(inst, variants); -if (!candidates.empty()) { - recipe_ordinal exemplar = best_shape_shifting_variant(inst, candidates); - trace(9992, "transform") << "found variant to specialize: " << exemplar << ' ' << get(Recipe, exemplar).name << end(); - recipe_ordinal new_recipe_ordinal = new_variant(exemplar, inst, caller_recipe); - if (new_recipe_ordinal == 0) goto skip_shape_shifting_variants; - variants.push_back(new_recipe_ordinal); // side-effect - recipe& variant = get(Recipe, new_recipe_ordinal); - // perform all transforms on the new specialization - if (!variant.steps.empty()) { - trace(9992, "transform") << "transforming new specialization: " << variant.name << end(); - for (int t = 0; t < SIZE(Transform); ++t) { - // one exception: skip tangle, which would have already occurred inside new_variant above - if (Transform.at(t) == insert_fragments) continue; - (*Transform.at(t))(new_recipe_ordinal); - } - } - variant.transformed_until = SIZE(Transform)-1; - trace(9992, "transform") << "new specialization: " << variant.name << end(); - return variant.name; -} -skip_shape_shifting_variants:; - -//: make sure we have no unspecialized shape-shifting recipes being called -//: before running mu programs - -:(before "End Instruction Operation Checks") -if (contains_key(Recipe, inst.operation) && inst.operation >= MAX_PRIMITIVE_RECIPES - && any_type_ingredient_in_header(inst.operation)) { - raise << maybe(caller.name) << "instruction " << inst.name << " has no valid specialization\n" << end(); - return; -} - -:(code) -// phase 2 of static dispatch -vector<recipe_ordinal> strictly_matching_shape_shifting_variants(const instruction& inst, vector<recipe_ordinal>& variants) { - vector<recipe_ordinal> result; - for (int i = 0; i < SIZE(variants); ++i) { - if (variants.at(i) == -1) continue; - if (!any_type_ingredient_in_header(variants.at(i))) continue; - if (all_concrete_header_reagents_strictly_match(inst, get(Recipe, variants.at(i)))) - result.push_back(variants.at(i)); - } - return result; -} - -bool all_concrete_header_reagents_strictly_match(const instruction& inst, const recipe& variant) { - if (SIZE(inst.ingredients) < SIZE(variant.ingredients)) { - trace(9993, "transform") << "too few ingredients" << end(); - return false; - } - if (SIZE(variant.products) < SIZE(inst.products)) { - trace(9993, "transform") << "too few products" << end(); - return false; - } - for (int i = 0; i < SIZE(variant.ingredients); ++i) { - if (!concrete_type_names_strictly_match(variant.ingredients.at(i), inst.ingredients.at(i))) { - trace(9993, "transform") << "concrete-type match failed: ingredient " << i << end(); - return false; - } - } - for (int i = 0; i < SIZE(inst.products); ++i) { - if (is_dummy(inst.products.at(i))) continue; - if (!concrete_type_names_strictly_match(variant.products.at(i), inst.products.at(i))) { - trace(9993, "transform") << "strict match failed: product " << i << end(); - return false; - } - } - return true; -} - -// tie-breaker for phase 2 -recipe_ordinal best_shape_shifting_variant(const instruction& inst, vector<recipe_ordinal>& candidates) { - assert(!candidates.empty()); - // primary score - int max_score = -1; - for (int i = 0; i < SIZE(candidates); ++i) { - int score = number_of_concrete_type_names(candidates.at(i)); - assert(score > -1); - if (score > max_score) max_score = score; - } - // break any ties at max_score by a secondary score - int min_score2 = 999; - int best_index = 0; - for (int i = 0; i < SIZE(candidates); ++i) { - int score1 = number_of_concrete_type_names(candidates.at(i)); - assert(score1 <= max_score); - if (score1 != max_score) continue; - const recipe& candidate = get(Recipe, candidates.at(i)); - int score2 = (SIZE(candidate.products)-SIZE(inst.products)) - + (SIZE(inst.ingredients)-SIZE(candidate.ingredients)); - assert(score2 < 999); - if (score2 < min_score2) { - min_score2 = score2; - best_index = i; - } - } - return candidates.at(best_index); -} - -bool any_type_ingredient_in_header(recipe_ordinal variant) { - const recipe& caller = get(Recipe, variant); - for (int i = 0; i < SIZE(caller.ingredients); ++i) { - if (contains_type_ingredient_name(caller.ingredients.at(i))) - return true; - } - for (int i = 0; i < SIZE(caller.products); ++i) { - if (contains_type_ingredient_name(caller.products.at(i))) - return true; - } - return false; -} - -bool concrete_type_names_strictly_match(reagent/*copy*/ to, reagent/*copy*/ from) { - canonize_type(to); - canonize_type(from); - return concrete_type_names_strictly_match(to.type, from.type, from); -} - -int number_of_concrete_type_names(recipe_ordinal r) { - const recipe& caller = get(Recipe, r); - int result = 0; - for (int i = 0; i < SIZE(caller.ingredients); ++i) - result += number_of_concrete_type_names(caller.ingredients.at(i)); - for (int i = 0; i < SIZE(caller.products); ++i) - result += number_of_concrete_type_names(caller.products.at(i)); - return result; -} - -int number_of_concrete_type_names(const reagent& r) { - return number_of_concrete_type_names(r.type); -} - -int number_of_concrete_type_names(const type_tree* type) { - if (!type) return 0; - int result = 0; - if (!type->name.empty() && !is_type_ingredient_name(type->name)) - result++; - result += number_of_concrete_type_names(type->left); - result += number_of_concrete_type_names(type->right); - return result; -} - -bool concrete_type_names_strictly_match(const type_tree* to, const type_tree* from, const reagent& rhs_reagent) { - if (!to) return !from; - if (!from) return !to; - if (is_type_ingredient_name(to->name)) return true; // type ingredient matches anything - if (to->name == "literal" && from->name == "literal") - return true; - if (to->name == "literal" - && Literal_type_names.find(from->name) != Literal_type_names.end()) - return true; - if (from->name == "literal" - && Literal_type_names.find(to->name) != Literal_type_names.end()) - return true; - if (from->name == "literal" && to->name == "address") - return rhs_reagent.name == "0"; - return to->name == from->name - && concrete_type_names_strictly_match(to->left, from->left, rhs_reagent) - && concrete_type_names_strictly_match(to->right, from->right, rhs_reagent); -} - -bool contains_type_ingredient_name(const reagent& x) { - return contains_type_ingredient_name(x.type); -} - -bool contains_type_ingredient_name(const type_tree* type) { - if (!type) return false; - if (is_type_ingredient_name(type->name)) return true; - return contains_type_ingredient_name(type->left) || contains_type_ingredient_name(type->right); -} - -recipe_ordinal new_variant(recipe_ordinal exemplar, const instruction& inst, const recipe& caller_recipe) { - string new_name = next_unused_recipe_name(inst.name); - assert(!contains_key(Recipe_ordinal, new_name)); - recipe_ordinal new_recipe_ordinal = put(Recipe_ordinal, new_name, Next_recipe_ordinal++); - // make a copy - assert(contains_key(Recipe, exemplar)); - assert(!contains_key(Recipe, new_recipe_ordinal)); - put(Recipe, new_recipe_ordinal, get(Recipe, exemplar)); - recipe& new_recipe = get(Recipe, new_recipe_ordinal); - new_recipe.name = new_name; - trace(9993, "transform") << "switching " << inst.name << " to specialized " << header_label(new_recipe_ordinal) << end(); - - // Replace type ingredients with concrete types in new_recipe. - // - // preprocessing: micro-manage a couple of transforms - // a) perform tangle *before* replacing type ingredients, just in case - // inserted code involves type ingredients - insert_fragments(new_recipe_ordinal); - // b) do the work of check_types_by_name while supporting type-ingredients - compute_type_names(new_recipe); - // that gives enough information to replace type-ingredients with concrete types - { - map<string, const type_tree*> mappings; - bool error = false; - compute_type_ingredient_mappings(get(Recipe, exemplar), inst, mappings, caller_recipe, &error); - if (!error) replace_type_ingredients(new_recipe, mappings); - for (map<string, const type_tree*>::iterator p = mappings.begin(); p != mappings.end(); ++p) - delete p->second; - if (error) return 0; // todo: delete new_recipe_ordinal from Recipes and other global state - } - ensure_all_concrete_types(new_recipe, get(Recipe, exemplar)); - return new_recipe_ordinal; -} - -void compute_type_names(recipe& variant) { - trace(9993, "transform") << "compute type names: " << variant.name << end(); - map<string, type_tree*> type_names; - for (int i = 0; i < SIZE(variant.ingredients); ++i) - save_or_deduce_type_name(variant.ingredients.at(i), type_names, variant); - for (int i = 0; i < SIZE(variant.products); ++i) - save_or_deduce_type_name(variant.products.at(i), type_names, variant); - for (int i = 0; i < SIZE(variant.steps); ++i) { - instruction& inst = variant.steps.at(i); - trace(9993, "transform") << " instruction: " << to_string(inst) << end(); - for (int in = 0; in < SIZE(inst.ingredients); ++in) - save_or_deduce_type_name(inst.ingredients.at(in), type_names, variant); - for (int out = 0; out < SIZE(inst.products); ++out) - save_or_deduce_type_name(inst.products.at(out), type_names, variant); - } -} - -void save_or_deduce_type_name(reagent& x, map<string, type_tree*>& type, const recipe& variant) { - trace(9994, "transform") << " checking " << to_string(x) << ": " << names_to_string(x.type) << end(); - if (!x.type && contains_key(type, x.name)) { - x.type = new type_tree(*get(type, x.name)); - trace(9994, "transform") << " deducing type to " << names_to_string(x.type) << end(); - return; - } - if (!x.type) { - raise << maybe(variant.original_name) << "unknown type for " << x.original_string << " (check the name for typos)\n" << end(); - return; - } - if (contains_key(type, x.name)) return; - if (x.type->name == "offset" || x.type->name == "variant") return; // special-case for container-access instructions - put(type, x.name, x.type); - trace(9993, "transform") << "type of " << x.name << " is " << names_to_string(x.type) << end(); -} - -void compute_type_ingredient_mappings(const recipe& exemplar, const instruction& inst, map<string, const type_tree*>& mappings, const recipe& caller_recipe, bool* error) { - int limit = min(SIZE(inst.ingredients), SIZE(exemplar.ingredients)); - for (int i = 0; i < limit; ++i) { - const reagent& exemplar_reagent = exemplar.ingredients.at(i); - reagent/*copy*/ ingredient = inst.ingredients.at(i); - canonize_type(ingredient); - if (is_mu_address(exemplar_reagent) && ingredient.name == "0") continue; // assume it matches - accumulate_type_ingredients(exemplar_reagent, ingredient, mappings, exemplar, inst, caller_recipe, error); - } - limit = min(SIZE(inst.products), SIZE(exemplar.products)); - for (int i = 0; i < limit; ++i) { - const reagent& exemplar_reagent = exemplar.products.at(i); - reagent/*copy*/ product = inst.products.at(i); - if (is_dummy(product)) continue; - canonize_type(product); - accumulate_type_ingredients(exemplar_reagent, product, mappings, exemplar, inst, caller_recipe, error); - } -} - -inline int min(int a, int b) { - return (a < b) ? a : b; -} - -void accumulate_type_ingredients(const reagent& exemplar_reagent, reagent& refinement, map<string, const type_tree*>& mappings, const recipe& exemplar, const instruction& call_instruction, const recipe& caller_recipe, bool* error) { - assert(refinement.type); - accumulate_type_ingredients(exemplar_reagent.type, refinement.type, mappings, exemplar, exemplar_reagent, call_instruction, caller_recipe, error); -} - -void accumulate_type_ingredients(const type_tree* exemplar_type, const type_tree* refinement_type, map<string, const type_tree*>& mappings, const recipe& exemplar, const reagent& exemplar_reagent, const instruction& call_instruction, const recipe& caller_recipe, bool* error) { - if (!exemplar_type) return; - if (!refinement_type) { - // todo: make this smarter; only flag an error if exemplar_type contains some *new* type ingredient - raise << maybe(exemplar.name) << "missing type ingredient for " << exemplar_reagent.original_string << '\n' << end(); - raise << " (called from '" << to_original_string(call_instruction) << "')\n" << end(); - return; - } - if (is_type_ingredient_name(exemplar_type->name)) { - const type_tree* curr_refinement_type = NULL; // temporary heap allocation; must always be deleted before it goes out of scope - if (refinement_type->left) - curr_refinement_type = new type_tree(*refinement_type->left); - else if (exemplar_type->right) - // splice out refinement_type->right, it'll be used later by the exemplar_type->right - curr_refinement_type = new type_tree(refinement_type->name, refinement_type->value, NULL); - else - curr_refinement_type = new type_tree(*refinement_type); - assert(!curr_refinement_type->left); - if (!contains_key(mappings, exemplar_type->name)) { - trace(9993, "transform") << "adding mapping from " << exemplar_type->name << " to " << to_string(curr_refinement_type) << end(); - put(mappings, exemplar_type->name, new type_tree(*curr_refinement_type)); - } - else { - if (!deeply_equal_type_names(get(mappings, exemplar_type->name), curr_refinement_type)) { - raise << maybe(caller_recipe.name) << "no call found for '" << to_original_string(call_instruction) << "'\n" << end(); - *error = true; - delete curr_refinement_type; - return; - } - if (get(mappings, exemplar_type->name)->name == "literal") { - delete get(mappings, exemplar_type->name); - put(mappings, exemplar_type->name, new type_tree(*curr_refinement_type)); - } - } - delete curr_refinement_type; - } - else { - accumulate_type_ingredients(exemplar_type->left, refinement_type->left, mappings, exemplar, exemplar_reagent, call_instruction, caller_recipe, error); - } - accumulate_type_ingredients(exemplar_type->right, refinement_type->right, mappings, exemplar, exemplar_reagent, call_instruction, caller_recipe, error); -} - -void replace_type_ingredients(recipe& new_recipe, const map<string, const type_tree*>& mappings) { - // update its header - if (mappings.empty()) return; - trace(9993, "transform") << "replacing in recipe header ingredients" << end(); - for (int i = 0; i < SIZE(new_recipe.ingredients); ++i) - replace_type_ingredients(new_recipe.ingredients.at(i), mappings, new_recipe); - trace(9993, "transform") << "replacing in recipe header products" << end(); - for (int i = 0; i < SIZE(new_recipe.products); ++i) - replace_type_ingredients(new_recipe.products.at(i), mappings, new_recipe); - // update its body - for (int i = 0; i < SIZE(new_recipe.steps); ++i) { - instruction& inst = new_recipe.steps.at(i); - trace(9993, "transform") << "replacing in instruction '" << to_string(inst) << "'" << end(); - for (int j = 0; j < SIZE(inst.ingredients); ++j) - replace_type_ingredients(inst.ingredients.at(j), mappings, new_recipe); - for (int j = 0; j < SIZE(inst.products); ++j) - replace_type_ingredients(inst.products.at(j), mappings, new_recipe); - // special-case for new: replace type ingredient in first ingredient *value* - if (inst.name == "new" && inst.ingredients.at(0).type->name != "literal-string") { - type_tree* type = parse_type_tree(inst.ingredients.at(0).name); - replace_type_ingredients(type, mappings); - inst.ingredients.at(0).name = inspect(type); - delete type; - } - } -} - -void replace_type_ingredients(reagent& x, const map<string, const type_tree*>& mappings, const recipe& caller) { - string before = to_string(x); - trace(9993, "transform") << "replacing in ingredient " << x.original_string << end(); - if (!x.type) { - raise << "specializing " << caller.original_name << ": missing type for " << x.original_string << '\n' << end(); - return; - } - replace_type_ingredients(x.type, mappings); -} - -// todo: too complicated and likely incomplete; maybe avoid replacing in place? -void replace_type_ingredients(type_tree* type, const map<string, const type_tree*>& mappings) { - if (!type) return; - if (contains_key(Type_ordinal, type->name)) // todo: ugly side effect - type->value = get(Type_ordinal, type->name); - if (!is_type_ingredient_name(type->name) || !contains_key(mappings, type->name)) { - replace_type_ingredients(type->left, mappings); - replace_type_ingredients(type->right, mappings); - return; - } - - const type_tree* replacement = get(mappings, type->name); - trace(9993, "transform") << type->name << " => " << names_to_string(replacement) << end(); - if (!contains_key(Type_ordinal, replacement->name)) { - // error in program; should be reported elsewhere - return; - } - - // type is a single type ingredient - assert(!type->left); - if (!type->right) assert(!replacement->left); - - if (!replacement->right) { - if (!replacement->left) { - type->name = (replacement->name == "literal") ? "number" : replacement->name; - type->value = get(Type_ordinal, type->name); - } - else { - type->name = ""; - type->value = 0; - type->left = new type_tree(*replacement); - } - replace_type_ingredients(type->right, mappings); - } - // replace non-last type? - else if (type->right) { - type->name = ""; - type->value = 0; - type->left = new type_tree(*replacement); - replace_type_ingredients(type->right, mappings); - } - // replace last type? - else { - type->name = replacement->name; - type->value = get(Type_ordinal, type->name); - type->right = new type_tree(*replacement->right); - } -} - -type_tree* parse_type_tree(const string& s) { - istringstream in(s); - in >> std::noskipws; - return parse_type_tree(in); -} - -type_tree* parse_type_tree(istream& in) { - skip_whitespace_but_not_newline(in); - if (!has_data(in)) return NULL; - if (in.peek() == ')') { - in.get(); - return NULL; - } - if (in.peek() != '(') - return new type_tree(next_word(in), 0); - in.get(); // skip '(' - type_tree* result = NULL; - type_tree** curr = &result; - while (in.peek() != ')') { - assert(has_data(in)); - *curr = new type_tree("", 0); - skip_whitespace_but_not_newline(in); - if (in.peek() == '(') - (*curr)->left = parse_type_tree(in); - else - (*curr)->name = next_word(in); - curr = &(*curr)->right; - } - in.get(); // skip ')' - return result; -} - -string inspect(const type_tree* x) { - ostringstream out; - dump_inspect(x, out); - return out.str(); -} - -void dump_inspect(const type_tree* x, ostream& out) { - if (!x->left && !x->right) { - out << x->name; - return; - } - out << '('; - for (const type_tree* curr = x; curr; curr = curr->right) { - if (curr != x) out << ' '; - if (curr->left) - dump_inspect(curr->left, out); - else - out << curr->name; - } - out << ')'; -} - -void ensure_all_concrete_types(/*const*/ recipe& new_recipe, const recipe& exemplar) { - for (int i = 0; i < SIZE(new_recipe.ingredients); ++i) - ensure_all_concrete_types(new_recipe.ingredients.at(i), exemplar); - for (int i = 0; i < SIZE(new_recipe.products); ++i) - ensure_all_concrete_types(new_recipe.products.at(i), exemplar); - for (int i = 0; i < SIZE(new_recipe.steps); ++i) { - instruction& inst = new_recipe.steps.at(i); - for (int j = 0; j < SIZE(inst.ingredients); ++j) - ensure_all_concrete_types(inst.ingredients.at(j), exemplar); - for (int j = 0; j < SIZE(inst.products); ++j) - ensure_all_concrete_types(inst.products.at(j), exemplar); - } -} - -void ensure_all_concrete_types(/*const*/ reagent& x, const recipe& exemplar) { - if (!x.type || contains_type_ingredient_name(x.type)) { - raise << maybe(exemplar.name) << "failed to map a type to " << x.original_string << '\n' << end(); - if (!x.type) x.type = new type_tree("", 0); // just to prevent crashes later - return; - } - if (x.type->value == -1) { - raise << maybe(exemplar.name) << "failed to map a type to the unknown " << x.original_string << '\n' << end(); - return; - } -} - -:(scenario shape_shifting_recipe_2) -def main [ - 10:point <- merge 14, 15 - 11:point <- foo 10:point -] -# non-matching shape-shifting variant -def foo a:_t, b:_t -> result:number [ - local-scope - load-ingredients - result <- copy 34 -] -# matching shape-shifting variant -def foo a:_t -> result:_t [ - local-scope - load-ingredients - result <- copy a -] -+mem: storing 14 in location 11 -+mem: storing 15 in location 12 - -:(scenario shape_shifting_recipe_nonroot) -def main [ - 10:foo:point <- merge 14, 15, 16 - 20:point/raw <- bar 10:foo:point -] -# shape-shifting recipe with type ingredient following some other type -def bar a:foo:_t -> result:_t [ - local-scope - load-ingredients - result <- get a, x:offset -] -container foo:_t [ - x:_t - y:number -] -+mem: storing 14 in location 20 -+mem: storing 15 in location 21 - -:(scenario shape_shifting_recipe_nested) -container c:_a:_b [ - a:_a - b:_b -] -def main [ - s:address:array:character <- new [abc] - {x: (c (address array character) number)} <- merge s, 34 - foo x -] -def foo x:c:_bar:_baz [ - local-scope - load-ingredients -] - -:(scenario shape_shifting_recipe_type_deduction_ignores_offsets) -def main [ - 10:foo:point <- merge 14, 15, 16 - 20:point/raw <- bar 10:foo:point -] -def bar a:foo:_t -> result:_t [ - local-scope - load-ingredients - x:number <- copy 1 - result <- get a, x:offset # shouldn't collide with other variable -] -container foo:_t [ - x:_t - y:number -] -+mem: storing 14 in location 20 -+mem: storing 15 in location 21 - -:(scenario shape_shifting_recipe_empty) -def main [ - foo 1 -] -# shape-shifting recipe with no body -def foo a:_t [ -] -# shouldn't crash - -:(scenario shape_shifting_recipe_handles_shape_shifting_new_ingredient) -def main [ - 1:address:foo:point <- bar 3 - 11:foo:point <- copy *1:address:foo:point -] -container foo:_t [ - x:_t - y:number -] -def bar x:number -> result:address:foo:_t [ - local-scope - load-ingredients - # new refers to _t in its ingredient *value* - result <- new {(foo _t) : type} -] -+mem: storing 0 in location 11 -+mem: storing 0 in location 12 -+mem: storing 0 in location 13 - -:(scenario shape_shifting_recipe_handles_shape_shifting_new_ingredient_2) -def main [ - 1:address:foo:point <- bar 3 - 11:foo:point <- copy *1:address:foo:point -] -def bar x:number -> result:address:foo:_t [ - local-scope - load-ingredients - # new refers to _t in its ingredient *value* - result <- new {(foo _t) : type} -] -# container defined after use -container foo:_t [ - x:_t - y:number -] -+mem: storing 0 in location 11 -+mem: storing 0 in location 12 -+mem: storing 0 in location 13 - -:(scenario shape_shifting_recipe_called_with_dummy) -def main [ - _ <- bar 34 -] -def bar x:_t -> result:address:_t [ - local-scope - load-ingredients - result <- copy 0 -] -$error: 0 - -:(code) -// this one needs a little more fine-grained control -void test_shape_shifting_new_ingredient_does_not_pollute_global_namespace() { - Trace_file = "shape_shifting_new_ingredient_does_not_pollute_global_namespace"; - - // if you specialize a shape-shifting recipe that allocates a type-ingredient.. - transform("def barz x:_elem [\n" - " local-scope\n" - " load-ingredients\n" - " y:address:number <- new _elem:type\n" - "]\n" - "def fooz [\n" - " local-scope\n" - " barz 34\n" - "]\n"); - // ..and if you then try to load a new shape-shifting container with that - // type-ingredient - run("container foo:_elem [\n" - " x:_elem\n" - " y:number\n" - "]\n"); - // then it should work as usual - reagent callsite("x:foo:point"); - reagent element = element_type(callsite.type, 0); - CHECK_EQ(element.name, "x"); - CHECK_EQ(element.type->name, "point"); - CHECK(!element.type->right); -} - -:(scenario shape_shifting_recipe_supports_compound_types) -def main [ - 1:address:point <- new point:type - *1:address:point <- put *1:address:point, y:offset, 34 - 3:address:point <- bar 1:address:point # specialize _t to address:point - 4:point <- copy *3:address:point -] -def bar a:_t -> result:_t [ - local-scope - load-ingredients - result <- copy a -] -+mem: storing 34 in location 5 - -:(scenario shape_shifting_recipe_error) -% Hide_errors = true; -def main [ - a:number <- copy 3 - b:address:number <- foo a -] -def foo a:_t -> b:_t [ - load-ingredients - b <- copy a -] -+error: main: no call found for 'b:address:number <- foo a' - -:(scenario specialize_inside_recipe_without_header) -def main [ - foo 3 -] -def foo [ - local-scope - x:number <- next-ingredient # ensure no header - 1:number/raw <- bar x # call a shape-shifting recipe -] -def bar x:_elem -> y:_elem [ - local-scope - load-ingredients - y <- add x, 1 -] -+mem: storing 4 in location 1 - -:(scenario specialize_with_literal) -def main [ - local-scope - # permit literal to map to number - 1:number/raw <- foo 3 -] -def foo x:_elem -> y:_elem [ - local-scope - load-ingredients - y <- add x, 1 -] -+mem: storing 4 in location 1 - -:(scenario specialize_with_literal_2) -def main [ - local-scope - # permit literal to map to character - 1:character/raw <- foo 3 -] -def foo x:_elem -> y:_elem [ - local-scope - load-ingredients - y <- add x, 1 -] -+mem: storing 4 in location 1 - -:(scenario specialize_with_literal_3) -def main [ - local-scope - # permit '0' to map to address to shape-shifting type-ingredient - 1:address:character/raw <- foo 0 -] -def foo x:address:_elem -> y:address:_elem [ - local-scope - load-ingredients - y <- copy x -] -+mem: storing 0 in location 1 -$error: 0 - -:(scenario specialize_with_literal_4) -% Hide_errors = true; -def main [ - local-scope - # ambiguous call: what's the type of its ingredient?! - foo 0 -] -def foo x:address:_elem -> y:address:_elem [ - local-scope - load-ingredients - y <- copy x -] -+error: main: instruction foo has no valid specialization - -:(scenario specialize_with_literal_5) -def main [ - foo 3, 4 # recipe mapping two variables to literals -] -def foo x:_elem, y:_elem [ - local-scope - load-ingredients - 1:number/raw <- add x, y -] -+mem: storing 7 in location 1 - -:(scenario multiple_shape_shifting_variants) -# try to call two different shape-shifting recipes with the same name -def main [ - e1:d1:number <- merge 3 - e2:d2:number <- merge 4, 5 - 1:number/raw <- foo e1 - 2:number/raw <- foo e2 -] -# the two shape-shifting definitions -def foo a:d1:_elem -> b:number [ - local-scope - load-ingredients - return 34 -] -def foo a:d2:_elem -> b:number [ - local-scope - load-ingredients - return 35 -] -# the shape-shifting containers they use -container d1:_elem [ - x:_elem -] -container d2:_elem [ - x:number - y:_elem -] -+mem: storing 34 in location 1 -+mem: storing 35 in location 2 - -:(scenario multiple_shape_shifting_variants_2) -# static dispatch between shape-shifting variants, _including pointer lookups_ -def main [ - e1:d1:number <- merge 3 - e2:address:d2:number <- new {(d2 number): type} - 1:number/raw <- foo e1 - 2:number/raw <- foo *e2 # different from previous scenario -] -def foo a:d1:_elem -> b:number [ - local-scope - load-ingredients - return 34 -] -def foo a:d2:_elem -> b:number [ - local-scope - load-ingredients - return 35 -] -container d1:_elem [ - x:_elem -] -container d2:_elem [ - x:number - y:_elem -] -+mem: storing 34 in location 1 -+mem: storing 35 in location 2 - -:(scenario missing_type_in_shape_shifting_recipe) -% Hide_errors = true; -def main [ - a:d1:number <- merge 3 - foo a -] -def foo a:d1:_elem -> b:number [ - local-scope - load-ingredients - copy e # no such variable - return 34 -] -container d1:_elem [ - x:_elem -] -+error: foo: unknown type for e (check the name for typos) -+error: specializing foo: missing type for e -# and it doesn't crash - -:(scenario missing_type_in_shape_shifting_recipe_2) -% Hide_errors = true; -def main [ - a:d1:number <- merge 3 - foo a -] -def foo a:d1:_elem -> b:number [ - local-scope - load-ingredients - get e, x:offset # unknown variable in a 'get', which does some extra checking - return 34 -] -container d1:_elem [ - x:_elem -] -+error: foo: unknown type for e (check the name for typos) -+error: specializing foo: missing type for e -# and it doesn't crash - -:(scenarios transform) -:(scenario specialize_recursive_shape_shifting_recipe) -def main [ - 1:number <- copy 34 - 2:number <- foo 1:number -] -def foo x:_elem -> y:number [ - local-scope - load-ingredients - { - break - y:number <- foo x - } - return y -] -+transform: new specialization: foo_2 -# transform terminates - -:(scenarios run) -:(scenario specialize_most_similar_variant) -def main [ - 1:address:number <- new number:type - 2:number <- foo 1:address:number -] -def foo x:_elem -> y:number [ - local-scope - load-ingredients - return 34 -] -def foo x:address:_elem -> y:number [ - local-scope - load-ingredients - return 35 -] -+mem: storing 35 in location 2 - -:(scenario specialize_most_similar_variant_2) -# version with headers padded with lots of unrelated concrete types -def main [ - 1:number <- copy 23 - 2:address:array:number <- copy 0 - 3:number <- foo 2:address:array:number, 1:number -] -# variant with concrete type -def foo dummy:address:array:number, x:number -> y:number, dummy:address:array:number [ - local-scope - load-ingredients - return 34 -] -# shape-shifting variant -def foo dummy:address:array:number, x:_elem -> y:number, dummy:address:array:number [ - local-scope - load-ingredients - return 35 -] -# prefer the concrete variant -+mem: storing 34 in location 3 - -:(scenario specialize_most_similar_variant_3) -def main [ - 1:address:array:character <- new [abc] - foo 1:address:array:character -] -def foo x:address:array:character [ - 2:number <- copy 34 -] -def foo x:address:_elem [ - 2:number <- copy 35 -] -# make sure the more precise version was used -+mem: storing 34 in location 2 - -:(scenario specialize_literal_as_number) -def main [ - 1:number <- foo 23 -] -def foo x:_elem -> y:number [ - local-scope - load-ingredients - return 34 -] -def foo x:character -> y:number [ - local-scope - load-ingredients - return 35 -] -+mem: storing 34 in location 1 - -:(scenario specialize_literal_as_number_2) -# version calling with literal -def main [ - 1:number <- foo 0 -] -# variant with concrete type -def foo x:number -> y:number [ - local-scope - load-ingredients - return 34 -] -# shape-shifting variant -def foo x:address:_elem -> y:number [ - local-scope - load-ingredients - return 35 -] -# prefer the concrete variant, ignore concrete types in scoring the shape-shifting variant -+mem: storing 34 in location 1 - -:(scenario specialize_literal_as_address) -def main [ - 1:number <- foo 0 -] -# variant with concrete address type -def foo x:address:number -> y:number [ - local-scope - load-ingredients - return 34 -] -# shape-shifting variant -def foo x:address:_elem -> y:number [ - local-scope - load-ingredients - return 35 -] -# prefer the concrete variant, ignore concrete types in scoring the shape-shifting variant -+mem: storing 34 in location 1 - -:(scenario missing_type_during_specialization) -% Hide_errors = true; -# define a shape-shifting recipe -def foo a:_elem [ -] -# define a container with field 'z' -container foo2 [ - z:number -] -def main [ - local-scope - x:foo2 <- merge 34 - y:number <- get x, z:offse # typo in 'offset' - # define a variable with the same name 'z' - z:number <- copy 34 - # trigger specialization of the shape-shifting recipe - foo z -] -# shouldn't crash - -:(scenario missing_type_during_specialization2) -% Hide_errors = true; -# define a shape-shifting recipe -def foo a:_elem [ -] -# define a container with field 'z' -container foo2 [ - z:number -] -def main [ - local-scope - x:foo2 <- merge 34 - y:number <- get x, z:offse # typo in 'offset' - # define a variable with the same name 'z' - z:address:number <- copy 34 - # trigger specialization of the shape-shifting recipe - foo *z -] -# shouldn't crash - -:(scenario tangle_shape_shifting_recipe) -# shape-shifting recipe -def foo a:_elem [ - local-scope - load-ingredients - <label1> -] -# tangle some code that refers to the type ingredient -after <label1> [ - b:_elem <- copy a -] -# trigger specialization -def main [ - local-scope - foo 34 -] -$exit: 0 |