From 4945e77a58f3ab10069461e86e5e10c06e91e489 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Mon, 29 May 2017 02:25:10 -0700 Subject: 3895 --- html/071recipe.cc.html | 513 +++++++++++++++++++++++++------------------------ 1 file changed, 260 insertions(+), 253 deletions(-) (limited to 'html/071recipe.cc.html') diff --git a/html/071recipe.cc.html b/html/071recipe.cc.html index c1fc9e30..8f011879 100644 --- a/html/071recipe.cc.html +++ b/html/071recipe.cc.html @@ -178,263 +178,270 @@ if ('onhashchange' in window) { 114 ¦ raise << maybe(current_recipe_name()) << "tried to call empty recipe in '" << to_string(current_instruction()) << "'" << end(); 115 ¦ break; 116 } -117 instruction/*copy*/ call_instruction = current_instruction(); -118 call_instruction.operation = ingredients.at(0).at(0); -119 call_instruction.ingredients.erase(call_instruction.ingredients.begin()); -120 Current_routine->calls.push_front(call(ingredients.at(0).at(0))); -121 ingredients.erase(ingredients.begin()); // drop the callee -122 finish_call_housekeeping(call_instruction, ingredients); -123 continue; -124 } -125 -126 //:: check types for 'call' instructions -127 -128 :(scenario call_check_literal_recipe) -129 % Hide_errors = true; -130 def main [ -131 1:num <- call f, 34 -132 ] -133 def f x:point -> y:point [ -134 local-scope -135 load-ingredients -136 y <- copy x -137 ] -138 +error: main: ingredient 0 has the wrong type at '1:num <- call f, 34' -139 +error: main: product 0 has the wrong type at '1:num <- call f, 34' -140 -141 :(scenario call_check_variable_recipe) -142 % Hide_errors = true; -143 def main [ -144 {1: (recipe point -> point)} <- copy f -145 2:num <- call {1: (recipe point -> point)}, 34 -146 ] -147 def f x:point -> y:point [ -148 local-scope -149 load-ingredients -150 y <- copy x -151 ] -152 +error: main: ingredient 0 has the wrong type at '2:num <- call {1: (recipe point -> point)}, 34' -153 +error: main: product 0 has the wrong type at '2:num <- call {1: (recipe point -> point)}, 34' -154 -155 :(after "Transform.push_back(check_instruction)") -156 Transform.push_back(check_indirect_calls_against_header); // idempotent -157 :(code) -158 void check_indirect_calls_against_header(const recipe_ordinal r) { -159 trace(9991, "transform") << "--- type-check 'call' instructions inside recipe " << get(Recipe, r).name << end(); -160 const recipe& caller = get(Recipe, r); -161 for (int i = 0; i < SIZE(caller.steps); ++i) { -162 ¦ const instruction& inst = caller.steps.at(i); -163 ¦ if (!is_indirect_call(inst.operation)) continue; -164 ¦ if (inst.ingredients.empty()) continue; // error raised above -165 ¦ const reagent& callee = inst.ingredients.at(0); -166 ¦ if (!is_mu_recipe(callee)) continue; // error raised above -167 ¦ const recipe callee_header = is_literal(callee) ? get(Recipe, callee.value) : from_reagent(inst.ingredients.at(0)); -168 ¦ if (!callee_header.has_header) continue; -169 ¦ if (is_indirect_call_with_ingredients(inst.operation)) { -170 ¦ ¦ for (long int i = /*skip callee*/1; i < min(SIZE(inst.ingredients), SIZE(callee_header.ingredients)+/*skip callee*/1); ++i) { -171 ¦ ¦ ¦ if (!types_coercible(callee_header.ingredients.at(i-/*skip callee*/1), inst.ingredients.at(i))) -172 ¦ ¦ ¦ ¦ raise << maybe(caller.name) << "ingredient " << i-/*skip callee*/1 << " has the wrong type at '" << to_original_string(inst) << "'\n" << end(); -173 ¦ ¦ } -174 ¦ } -175 ¦ if (is_indirect_call_with_products(inst.operation)) { -176 ¦ ¦ for (long int i = 0; i < min(SIZE(inst.products), SIZE(callee_header.products)); ++i) { -177 ¦ ¦ ¦ if (is_dummy(inst.products.at(i))) continue; -178 ¦ ¦ ¦ if (!types_coercible(callee_header.products.at(i), inst.products.at(i))) -179 ¦ ¦ ¦ ¦ raise << maybe(caller.name) << "product " << i << " has the wrong type at '" << to_original_string(inst) << "'\n" << end(); +117 const call& caller_frame = current_call(); +118 instruction/*copy*/ call_instruction = to_instruction(caller_frame); +119 call_instruction.operation = ingredients.at(0).at(0); +120 call_instruction.ingredients.erase(call_instruction.ingredients.begin()); +121 Current_routine->calls.push_front(call(ingredients.at(0).at(0))); +122 ingredients.erase(ingredients.begin()); // drop the callee +123 finish_call_housekeeping(call_instruction, ingredients); +124 Num_refcount_updates[caller_frame.running_recipe][caller_frame.running_step_index] +125 ¦ ¦ += (Total_refcount_updates - initial_num_refcount_updates); +126 initial_num_refcount_updates = Total_refcount_updates; +127 // not done with caller +128 write_products = false; +129 fall_through_to_next_instruction = false; +130 break; +131 } +132 +133 //:: check types for 'call' instructions +134 +135 :(scenario call_check_literal_recipe) +136 % Hide_errors = true; +137 def main [ +138 1:num <- call f, 34 +139 ] +140 def f x:point -> y:point [ +141 local-scope +142 load-ingredients +143 y <- copy x +144 ] +145 +error: main: ingredient 0 has the wrong type at '1:num <- call f, 34' +146 +error: main: product 0 has the wrong type at '1:num <- call f, 34' +147 +148 :(scenario call_check_variable_recipe) +149 % Hide_errors = true; +150 def main [ +151 {1: (recipe point -> point)} <- copy f +152 2:num <- call {1: (recipe point -> point)}, 34 +153 ] +154 def f x:point -> y:point [ +155 local-scope +156 load-ingredients +157 y <- copy x +158 ] +159 +error: main: ingredient 0 has the wrong type at '2:num <- call {1: (recipe point -> point)}, 34' +160 +error: main: product 0 has the wrong type at '2:num <- call {1: (recipe point -> point)}, 34' +161 +162 :(after "Transform.push_back(check_instruction)") +163 Transform.push_back(check_indirect_calls_against_header); // idempotent +164 :(code) +165 void check_indirect_calls_against_header(const recipe_ordinal r) { +166 trace(9991, "transform") << "--- type-check 'call' instructions inside recipe " << get(Recipe, r).name << end(); +167 const recipe& caller = get(Recipe, r); +168 for (int i = 0; i < SIZE(caller.steps); ++i) { +169 ¦ const instruction& inst = caller.steps.at(i); +170 ¦ if (!is_indirect_call(inst.operation)) continue; +171 ¦ if (inst.ingredients.empty()) continue; // error raised above +172 ¦ const reagent& callee = inst.ingredients.at(0); +173 ¦ if (!is_mu_recipe(callee)) continue; // error raised above +174 ¦ const recipe callee_header = is_literal(callee) ? get(Recipe, callee.value) : from_reagent(inst.ingredients.at(0)); +175 ¦ if (!callee_header.has_header) continue; +176 ¦ if (is_indirect_call_with_ingredients(inst.operation)) { +177 ¦ ¦ for (long int i = /*skip callee*/1; i < min(SIZE(inst.ingredients), SIZE(callee_header.ingredients)+/*skip callee*/1); ++i) { +178 ¦ ¦ ¦ if (!types_coercible(callee_header.ingredients.at(i-/*skip callee*/1), inst.ingredients.at(i))) +179 ¦ ¦ ¦ ¦ raise << maybe(caller.name) << "ingredient " << i-/*skip callee*/1 << " has the wrong type at '" << to_original_string(inst) << "'\n" << end(); 180 ¦ ¦ } 181 ¦ } -182 } -183 } -184 -185 bool is_indirect_call(const recipe_ordinal r) { -186 return is_indirect_call_with_ingredients(r) || is_indirect_call_with_products(r); -187 } -188 -189 bool is_indirect_call_with_ingredients(const recipe_ordinal r) { -190 if (r == CALL) return true; -191 // End is_indirect_call_with_ingredients Special-cases -192 return false; -193 } -194 bool is_indirect_call_with_products(const recipe_ordinal r) { -195 if (r == CALL) return true; -196 // End is_indirect_call_with_products Special-cases -197 return false; -198 } -199 -200 recipe from_reagent(const reagent& r) { -201 assert(r.type); -202 recipe result_header; // will contain only ingredients and products, nothing else -203 result_header.has_header = true; -204 if (r.type->atom) { -205 ¦ assert(r.type->name == "recipe"); -206 ¦ return result_header; -207 } -208 const type_tree* root_type = r.type->atom ? r.type : r.type->left; -209 assert(root_type->atom); -210 assert(root_type->name == "recipe"); -211 const type_tree* curr = r.type->right; -212 for (/*nada*/; curr && !curr->atom; curr = curr->right) { -213 ¦ if (curr->left->atom && curr->left->name == "->") { -214 ¦ ¦ curr = curr->right; // skip delimiter -215 ¦ ¦ goto read_products; -216 ¦ } -217 ¦ result_header.ingredients.push_back(next_recipe_reagent(curr->left)); -218 } -219 if (curr) { -220 ¦ assert(curr->atom); -221 ¦ result_header.ingredients.push_back(next_recipe_reagent(curr)); -222 ¦ return result_header; // no products -223 } -224 read_products: -225 for (/*nada*/; curr && !curr->atom; curr = curr->right) -226 ¦ result_header.products.push_back(next_recipe_reagent(curr->left)); -227 if (curr) { -228 ¦ assert(curr->atom); -229 ¦ result_header.products.push_back(next_recipe_reagent(curr)); +182 ¦ if (is_indirect_call_with_products(inst.operation)) { +183 ¦ ¦ for (long int i = 0; i < min(SIZE(inst.products), SIZE(callee_header.products)); ++i) { +184 ¦ ¦ ¦ if (is_dummy(inst.products.at(i))) continue; +185 ¦ ¦ ¦ if (!types_coercible(callee_header.products.at(i), inst.products.at(i))) +186 ¦ ¦ ¦ ¦ raise << maybe(caller.name) << "product " << i << " has the wrong type at '" << to_original_string(inst) << "'\n" << end(); +187 ¦ ¦ } +188 ¦ } +189 } +190 } +191 +192 bool is_indirect_call(const recipe_ordinal r) { +193 return is_indirect_call_with_ingredients(r) || is_indirect_call_with_products(r); +194 } +195 +196 bool is_indirect_call_with_ingredients(const recipe_ordinal r) { +197 if (r == CALL) return true; +198 // End is_indirect_call_with_ingredients Special-cases +199 return false; +200 } +201 bool is_indirect_call_with_products(const recipe_ordinal r) { +202 if (r == CALL) return true; +203 // End is_indirect_call_with_products Special-cases +204 return false; +205 } +206 +207 recipe from_reagent(const reagent& r) { +208 assert(r.type); +209 recipe result_header; // will contain only ingredients and products, nothing else +210 result_header.has_header = true; +211 if (r.type->atom) { +212 ¦ assert(r.type->name == "recipe"); +213 ¦ return result_header; +214 } +215 const type_tree* root_type = r.type->atom ? r.type : r.type->left; +216 assert(root_type->atom); +217 assert(root_type->name == "recipe"); +218 const type_tree* curr = r.type->right; +219 for (/*nada*/; curr && !curr->atom; curr = curr->right) { +220 ¦ if (curr->left->atom && curr->left->name == "->") { +221 ¦ ¦ curr = curr->right; // skip delimiter +222 ¦ ¦ goto read_products; +223 ¦ } +224 ¦ result_header.ingredients.push_back(next_recipe_reagent(curr->left)); +225 } +226 if (curr) { +227 ¦ assert(curr->atom); +228 ¦ result_header.ingredients.push_back(next_recipe_reagent(curr)); +229 ¦ return result_header; // no products 230 } -231 return result_header; -232 } -233 -234 :(before "End Unit Tests") -235 void test_from_reagent_atomic() { -236 reagent a("{f: recipe}"); -237 recipe r_header = from_reagent(a); -238 CHECK(r_header.ingredients.empty()); -239 CHECK(r_header.products.empty()); -240 } -241 void test_from_reagent_non_atomic() { -242 reagent a("{f: (recipe number -> number)}"); -243 recipe r_header = from_reagent(a); -244 CHECK_EQ(SIZE(r_header.ingredients), 1); -245 CHECK_EQ(SIZE(r_header.products), 1); -246 } -247 void test_from_reagent_reads_ingredient_at_end() { -248 reagent a("{f: (recipe number number)}"); -249 recipe r_header = from_reagent(a); -250 CHECK_EQ(SIZE(r_header.ingredients), 2); -251 CHECK(r_header.products.empty()); -252 } -253 void test_from_reagent_reads_sole_ingredient_at_end() { -254 reagent a("{f: (recipe number)}"); -255 recipe r_header = from_reagent(a); -256 CHECK_EQ(SIZE(r_header.ingredients), 1); -257 CHECK(r_header.products.empty()); -258 } -259 -260 :(code) -261 reagent next_recipe_reagent(const type_tree* curr) { -262 if (!curr->left) return reagent("recipe:"+curr->name); -263 reagent result; -264 result.name = "recipe"; -265 result.type = new type_tree(*curr); -266 return result; -267 } -268 -269 bool is_mu_recipe(const reagent& r) { -270 if (!r.type) return false; -271 if (r.type->atom) -272 ¦ return r.type->name == "recipe-literal"; -273 if (!r.type->left->atom) return false; -274 if (r.type->left->name == "recipe") return true; -275 return false; -276 } -277 -278 :(scenario copy_typecheck_recipe_variable) -279 % Hide_errors = true; -280 def main [ -281 3:num <- copy 34 # abc def -282 {1: (recipe number -> number)} <- copy f # store literal in a matching variable -283 {2: (recipe boolean -> boolean)} <- copy {1: (recipe number -> number)} # mismatch between recipe variables -284 ] -285 def f x:num -> y:num [ -286 local-scope -287 load-ingredients -288 y <- copy x -289 ] -290 +error: main: can't copy '{1: (recipe number -> number)}' to '{2: (recipe boolean -> boolean)}'; types don't match -291 -292 :(scenario copy_typecheck_recipe_variable_2) -293 % Hide_errors = true; -294 def main [ -295 {1: (recipe number -> number)} <- copy f # mismatch with a recipe literal +231 read_products: +232 for (/*nada*/; curr && !curr->atom; curr = curr->right) +233 ¦ result_header.products.push_back(next_recipe_reagent(curr->left)); +234 if (curr) { +235 ¦ assert(curr->atom); +236 ¦ result_header.products.push_back(next_recipe_reagent(curr)); +237 } +238 return result_header; +239 } +240 +241 :(before "End Unit Tests") +242 void test_from_reagent_atomic() { +243 reagent a("{f: recipe}"); +244 recipe r_header = from_reagent(a); +245 CHECK(r_header.ingredients.empty()); +246 CHECK(r_header.products.empty()); +247 } +248 void test_from_reagent_non_atomic() { +249 reagent a("{f: (recipe number -> number)}"); +250 recipe r_header = from_reagent(a); +251 CHECK_EQ(SIZE(r_header.ingredients), 1); +252 CHECK_EQ(SIZE(r_header.products), 1); +253 } +254 void test_from_reagent_reads_ingredient_at_end() { +255 reagent a("{f: (recipe number number)}"); +256 recipe r_header = from_reagent(a); +257 CHECK_EQ(SIZE(r_header.ingredients), 2); +258 CHECK(r_header.products.empty()); +259 } +260 void test_from_reagent_reads_sole_ingredient_at_end() { +261 reagent a("{f: (recipe number)}"); +262 recipe r_header = from_reagent(a); +263 CHECK_EQ(SIZE(r_header.ingredients), 1); +264 CHECK(r_header.products.empty()); +265 } +266 +267 :(code) +268 reagent next_recipe_reagent(const type_tree* curr) { +269 if (!curr->left) return reagent("recipe:"+curr->name); +270 reagent result; +271 result.name = "recipe"; +272 result.type = new type_tree(*curr); +273 return result; +274 } +275 +276 bool is_mu_recipe(const reagent& r) { +277 if (!r.type) return false; +278 if (r.type->atom) +279 ¦ return r.type->name == "recipe-literal"; +280 if (!r.type->left->atom) return false; +281 if (r.type->left->name == "recipe") return true; +282 return false; +283 } +284 +285 :(scenario copy_typecheck_recipe_variable) +286 % Hide_errors = true; +287 def main [ +288 3:num <- copy 34 # abc def +289 {1: (recipe number -> number)} <- copy f # store literal in a matching variable +290 {2: (recipe boolean -> boolean)} <- copy {1: (recipe number -> number)} # mismatch between recipe variables +291 ] +292 def f x:num -> y:num [ +293 local-scope +294 load-ingredients +295 y <- copy x 296 ] -297 def f x:bool -> y:bool [ -298 local-scope -299 load-ingredients -300 y <- copy x -301 ] -302 +error: main: can't copy 'f' to '{1: (recipe number -> number)}'; types don't match -303 -304 :(before "End Matching Types For Literal(to)") -305 if (is_mu_recipe(to)) { -306 if (!contains_key(Recipe, from.value)) { -307 ¦ raise << "trying to store recipe " << from.name << " into " << to_string(to) << " but there's no such recipe\n" << end(); -308 ¦ return false; -309 } -310 const recipe& rrhs = get(Recipe, from.value); -311 const recipe& rlhs = from_reagent(to); -312 for (long int i = 0; i < min(SIZE(rlhs.ingredients), SIZE(rrhs.ingredients)); ++i) { -313 ¦ if (!types_match(rlhs.ingredients.at(i), rrhs.ingredients.at(i))) -314 ¦ ¦ return false; -315 } -316 for (long int i = 0; i < min(SIZE(rlhs.products), SIZE(rrhs.products)); ++i) { -317 ¦ if (!types_match(rlhs.products.at(i), rrhs.products.at(i))) -318 ¦ ¦ return false; -319 } -320 return true; -321 } -322 -323 :(scenario call_variable_compound_ingredient) -324 def main [ -325 {1: (recipe (address number) -> number)} <- copy f -326 2:&:num <- copy 0 -327 3:num <- call {1: (recipe (address number) -> number)}, 2:&:num -328 ] -329 def f x:&:num -> y:num [ -330 local-scope -331 load-ingredients -332 y <- copy x -333 ] -334 $error: 0 -335 -336 //: make sure we don't accidentally break on a function literal -337 :(scenario jump_forbidden_on_recipe_literals) -338 % Hide_errors = true; -339 def foo [ -340 local-scope -341 ] -342 def main [ -343 local-scope -344 { -345 ¦ break-if foo -346 } -347 ] -348 # error should be as if foo is not a recipe -349 +error: main: missing type for 'foo' in 'break-if foo' -350 -351 :(before "End JUMP_IF Checks") -352 check_for_recipe_literals(inst, get(Recipe, r)); -353 :(before "End JUMP_UNLESS Checks") -354 check_for_recipe_literals(inst, get(Recipe, r)); -355 :(code) -356 void check_for_recipe_literals(const instruction& inst, const recipe& caller) { -357 for (int i = 0; i < SIZE(inst.ingredients); ++i) { -358 ¦ if (is_mu_recipe(inst.ingredients.at(i))) { -359 ¦ ¦ raise << maybe(caller.name) << "missing type for '" << inst.ingredients.at(i).original_string << "' in '" << to_original_string(inst) << "'\n" << end(); -360 ¦ ¦ if (is_present_in_ingredients(caller, inst.ingredients.at(i).name)) -361 ¦ ¦ ¦ raise << " did you forget 'load-ingredients'?\n" << end(); -362 ¦ } -363 } -364 } -365 -366 :(scenario load_ingredients_missing_error_3) -367 % Hide_errors = true; -368 def foo {f: (recipe num -> num)} [ -369 local-scope -370 b:num <- call f, 1 -371 ] -372 +error: foo: missing type for 'f' in 'b:num <- call f, 1' -373 +error: did you forget 'load-ingredients'? +297 +error: main: can't copy '{1: (recipe number -> number)}' to '{2: (recipe boolean -> boolean)}'; types don't match +298 +299 :(scenario copy_typecheck_recipe_variable_2) +300 % Hide_errors = true; +301 def main [ +302 {1: (recipe number -> number)} <- copy f # mismatch with a recipe literal +303 ] +304 def f x:bool -> y:bool [ +305 local-scope +306 load-ingredients +307 y <- copy x +308 ] +309 +error: main: can't copy 'f' to '{1: (recipe number -> number)}'; types don't match +310 +311 :(before "End Matching Types For Literal(to)") +312 if (is_mu_recipe(to)) { +313 if (!contains_key(Recipe, from.value)) { +314 ¦ raise << "trying to store recipe " << from.name << " into " << to_string(to) << " but there's no such recipe\n" << end(); +315 ¦ return false; +316 } +317 const recipe& rrhs = get(Recipe, from.value); +318 const recipe& rlhs = from_reagent(to); +319 for (long int i = 0; i < min(SIZE(rlhs.ingredients), SIZE(rrhs.ingredients)); ++i) { +320 ¦ if (!types_match(rlhs.ingredients.at(i), rrhs.ingredients.at(i))) +321 ¦ ¦ return false; +322 } +323 for (long int i = 0; i < min(SIZE(rlhs.products), SIZE(rrhs.products)); ++i) { +324 ¦ if (!types_match(rlhs.products.at(i), rrhs.products.at(i))) +325 ¦ ¦ return false; +326 } +327 return true; +328 } +329 +330 :(scenario call_variable_compound_ingredient) +331 def main [ +332 {1: (recipe (address number) -> number)} <- copy f +333 2:&:num <- copy 0 +334 3:num <- call {1: (recipe (address number) -> number)}, 2:&:num +335 ] +336 def f x:&:num -> y:num [ +337 local-scope +338 load-ingredients +339 y <- copy x +340 ] +341 $error: 0 +342 +343 //: make sure we don't accidentally break on a function literal +344 :(scenario jump_forbidden_on_recipe_literals) +345 % Hide_errors = true; +346 def foo [ +347 local-scope +348 ] +349 def main [ +350 local-scope +351 { +352 ¦ break-if foo +353 } +354 ] +355 # error should be as if foo is not a recipe +356 +error: main: missing type for 'foo' in 'break-if foo' +357 +358 :(before "End JUMP_IF Checks") +359 check_for_recipe_literals(inst, get(Recipe, r)); +360 :(before "End JUMP_UNLESS Checks") +361 check_for_recipe_literals(inst, get(Recipe, r)); +362 :(code) +363 void check_for_recipe_literals(const instruction& inst, const recipe& caller) { +364 for (int i = 0; i < SIZE(inst.ingredients); ++i) { +365 ¦ if (is_mu_recipe(inst.ingredients.at(i))) { +366 ¦ ¦ raise << maybe(caller.name) << "missing type for '" << inst.ingredients.at(i).original_string << "' in '" << to_original_string(inst) << "'\n" << end(); +367 ¦ ¦ if (is_present_in_ingredients(caller, inst.ingredients.at(i).name)) +368 ¦ ¦ ¦ raise << " did you forget 'load-ingredients'?\n" << end(); +369 ¦ } +370 } +371 } +372 +373 :(scenario load_ingredients_missing_error_3) +374 % Hide_errors = true; +375 def foo {f: (recipe num -> num)} [ +376 local-scope +377 b:num <- call f, 1 +378 ] +379 +error: foo: missing type for 'f' in 'b:num <- call f, 1' +380 +error: did you forget 'load-ingredients'? -- cgit 1.4.1-2-gfad0