From d009e158803956c76adbf8f58a62884c3e7affb3 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Tue, 26 Jan 2016 23:47:23 -0800 Subject: 2605 --- html/056recipe_header.cc.html | 135 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 127 insertions(+), 8 deletions(-) (limited to 'html/056recipe_header.cc.html') diff --git a/html/056recipe_header.cc.html b/html/056recipe_header.cc.html index 08cc8fdd..48da455e 100644 --- a/html/056recipe_header.cc.html +++ b/html/056recipe_header.cc.html @@ -175,15 +175,133 @@ if (result.has_hea :(before "End Rewrite Instruction(curr, recipe result)") if (curr.name == "load-ingredients") { curr.clear(); + recipe_ordinal op = get(Recipe_ordinal, "next-ingredient-without-typechecking"); for (long long int i = 0; i < SIZE(result.ingredients); ++i) { - curr.operation = get(Recipe_ordinal, "next-ingredient"); - curr.name = "next-ingredient"; + curr.operation = op; + curr.name = "next-ingredient-without-typechecking"; curr.products.push_back(result.ingredients.at(i)); result.steps.push_back(curr); curr.clear(); } } +//: internal version of next-ingredient; don't call this directly +:(before "End Primitive Recipe Declarations") +NEXT_INGREDIENT_WITHOUT_TYPECHECKING, +:(before "End Primitive Recipe Numbers") +put(Recipe_ordinal, "next-ingredient-without-typechecking", NEXT_INGREDIENT_WITHOUT_TYPECHECKING); +:(before "End Primitive Recipe Checks") +case NEXT_INGREDIENT_WITHOUT_TYPECHECKING: { + break; +} +:(before "End Primitive Recipe Implementations") +case NEXT_INGREDIENT_WITHOUT_TYPECHECKING: { + assert(!Current_routine->calls.empty()); + if (current_call().next_ingredient_to_process < SIZE(current_call().ingredient_atoms)) { + products.push_back( + current_call().ingredient_atoms.at(current_call().next_ingredient_to_process)); + assert(SIZE(products) == 1); products.resize(2); // push a new vector + products.at(1).push_back(1); + ++current_call().next_ingredient_to_process; + } + else { + products.resize(2); + // pad the first product with sufficient zeros to match its type + long long int size = size_of(current_instruction().products.at(0)); + for (long long int i = 0; i < size; ++i) + products.at(0).push_back(0); + products.at(1).push_back(0); + } + break; +} + +//:: Check all calls against headers. + +:(scenario show_clear_error_on_bad_call) +% Hide_errors = true; +recipe main [ + 1:number <- foo 34 +] +recipe foo x:boolean -> y:number [ + local-scope + load-ingredients + reply 35 +] ++error: main: ingredient 0 has the wrong type at '1:number <- foo 34' + +:(scenario show_clear_error_on_bad_call_2) +% Hide_errors = true; +recipe main [ + 1:boolean <- foo 34 +] +recipe foo x:number -> y:number [ + local-scope + load-ingredients + reply x +] ++error: main: product 0 has the wrong type at '1:boolean <- foo 34' + +:(after "Transform.push_back(check_instruction)") +Transform.push_back(check_calls_against_header); // idempotent +:(code) +void check_calls_against_header(const recipe_ordinal r) { + trace(9991, "transform") << "--- type-check calls inside recipe " << get(Recipe, r).name << end(); + const recipe& caller = get(Recipe, r); + for (long long int i = 0; i < SIZE(caller.steps); ++i) { + const instruction& inst = caller.steps.at(i); + if (inst.operation < MAX_PRIMITIVE_RECIPES) continue; + const recipe& callee = get(Recipe, inst.operation); + if (!callee.has_header) continue; + for (long int i = 0; i < min(SIZE(inst.ingredients), SIZE(callee.ingredients)); ++i) { + if (!types_coercible(callee.ingredients.at(i), inst.ingredients.at(i))) + raise_error << maybe(caller.name) << "ingredient " << i << " has the wrong type at '" << inst.to_string() << "'\n" << end(); + if (is_unique_address(inst.ingredients.at(i))) + raise << maybe(caller.name) << "try to avoid passing non-shared addresses into calls, like ingredient " << i << " at '" << inst.to_string() << "'\n" << end(); + } + for (long int i = 0; i < min(SIZE(inst.products), SIZE(callee.products)); ++i) { + if (is_dummy(inst.products.at(i))) continue; + if (!types_coercible(callee.products.at(i), inst.products.at(i))) + raise_error << maybe(caller.name) << "product " << i << " has the wrong type at '" << inst.to_string() << "'\n" << end(); + if (is_unique_address(inst.products.at(i))) + raise << maybe(caller.name) << "try to avoid getting non-shared addresses out of calls, like product " << i << " at '" << inst.to_string() << "'\n" << end(); + } + } +} + +bool is_unique_address(reagent x) { + if (!canonize_type(x)) return false; + if (!x.type) return false; + if (x.type->value != get(Type_ordinal, "address")) return false; + if (!x.type->right) return true; + return x.type->right->value != get(Type_ordinal, "shared"); +} + +//: additionally, warn on calls receiving non-shared addresses + +:(scenario warn_on_calls_with_addresses) +% Hide_warnings= true; +recipe main [ + 1:address:number <- copy 3/unsafe + foo 1:address:number +] +recipe foo x:address:number [ + local-scope + load-ingredients +] ++warn: main: try to avoid passing non-shared addresses into calls, like ingredient 0 at 'foo 1:address:number' + +:(scenario warn_on_calls_with_addresses_2) +% Hide_warnings= true; +recipe main [ + 1:address:number <- foo +] +recipe foo -> x:address:number [ + local-scope + load-ingredients + x <- copy 0 +] ++warn: main: try to avoid getting non-shared addresses out of calls, like product 0 at '1:address:number <- foo ' + //:: Check types going in and out of all recipes with headers. :(scenarios transform) @@ -235,6 +353,7 @@ Transform.push_back((const recipe_ordinal r) { recipe& caller_recipe = get(Recipe, r); if (caller_recipe.products.empty()) return; + caller_recipe.ingredient_index.clear(); trace(9991, "transform") << "--- checking reply instructions against header for " << caller_recipe.name << end(); for (long long int i = 0; i < SIZE(caller_recipe.ingredients); ++i) { if (contains_key(caller_recipe.ingredient_index, caller_recipe.ingredients.at(i).name)) @@ -284,10 +403,8 @@ void deduce_types_from_header(const recipe_ordina trace(9992, "transform") << "instruction: " << inst.to_string() << end(); for (long long int i = 0; i < SIZE(inst.ingredients); ++i) { if (inst.ingredients.at(i).type) continue; - if (header_type.find(inst.ingredients.at(i).name) == header_type.end()) { - raise << maybe(caller_recipe.name) << "unknown variable " << inst.ingredients.at(i).name << " in '" << inst.to_string() << "'\n" << end(); + if (header_type.find(inst.ingredients.at(i).name) == header_type.end()) continue; - } if (!inst.ingredients.at(i).type) inst.ingredients.at(i).type = new type_tree(*get(header_type, inst.ingredients.at(i).name)); if (!inst.ingredients.at(i).properties.at(0).second) @@ -297,10 +414,8 @@ void deduce_types_from_header(const recipe_ordina for (long long int i = 0; i < SIZE(inst.products); ++i) { trace(9993, "transform") << " product: " << debug_string(inst.products.at(i)) << end(); if (inst.products.at(i).type) continue; - if (header_type.find(inst.products.at(i).name) == header_type.end()) { - raise << maybe(caller_recipe.name) << "unknown variable " << inst.products.at(i).name << " in '" << inst.to_string() << "'\n" << end(); + if (header_type.find(inst.products.at(i).name) == header_type.end()) continue; - } if (!inst.products.at(i).type) inst.products.at(i).type = new type_tree(*get(header_type, inst.products.at(i).name)); if (!inst.products.at(i).properties.at(0).second) @@ -415,6 +530,10 @@ recipe add2 x:number, y:number +error: main: '3:number <- add2 1:number, 2:number' should write to 1:number rather than 3:number + +:(before "End Includes") +using std::min; +using std::max; -- cgit 1.4.1-2-gfad0