From 691b529e53a608c0837599f2562897e01fda1888 Mon Sep 17 00:00:00 2001 From: "Kartik K. Agaram" Date: Sun, 29 Nov 2015 11:43:25 -0800 Subject: 2607 - resolve some edge cases in static dispatch --- 021check_instruction.cc | 25 ++++++++++------ 035call_ingredient.cc | 1 + 048check_type_by_name.cc | 2 +- 057static_dispatch.cc | 76 ++++++++++++++++++++++++++++++++++++++++++++---- 999spaces.cc | 13 +++++++++ 5 files changed, 101 insertions(+), 16 deletions(-) diff --git a/021check_instruction.cc b/021check_instruction.cc index 79022e2a..400ef96d 100644 --- a/021check_instruction.cc +++ b/021check_instruction.cc @@ -82,7 +82,7 @@ recipe main [ $error: 0 :(code) -// types_match with some special-cases +// types_match with some leniency bool types_coercible(const reagent& lhs, const reagent& rhs) { if (types_match(lhs, rhs)) return true; if (is_mu_address(rhs) && is_mu_number(lhs)) return true; @@ -99,34 +99,41 @@ bool types_match(const reagent& lhs, const reagent& rhs) { // End Matching Types For Literal(lhs) // allow writing 0 to any address if (is_mu_address(lhs)) return rhs.name == "0"; + if (!lhs.type) return false; + if (lhs.type->value == get(Type_ordinal, "boolean")) + return boolean_matches_literal(lhs, rhs); return size_of(lhs) == 1; // literals are always scalars } return types_strictly_match(lhs, rhs); } +bool boolean_matches_literal(const reagent& lhs, const reagent& rhs) { + if (!is_literal(rhs)) return false; + if (!lhs.type) return false; + if (lhs.type->value != get(Type_ordinal, "boolean")) return false; + return rhs.name == "0" || rhs.name == "1"; +} + // copy arguments because later layers will want to make changes to them // without perturbing the caller bool types_strictly_match(reagent lhs, reagent rhs) { + if (is_literal(rhs) && lhs.type->value == get(Type_ordinal, "number")) return true; // to sidestep type-checking, use /unsafe in the source. // this will be highlighted in red inside vim. just for setting up some tests. if (is_unsafe(rhs)) return true; // '_' never raises type error if (is_dummy(lhs)) return true; if (!lhs.type) return !rhs.type; - return types_match(lhs.type, rhs.type); + return types_strictly_match(lhs.type, rhs.type); } // two types match if the second begins like the first // (trees perform the same check recursively on each subtree) -bool types_match(type_tree* lhs, type_tree* rhs) { +bool types_strictly_match(type_tree* lhs, type_tree* rhs) { if (!lhs) return true; - if (!rhs || rhs->value == 0) { - if (lhs->value == get(Type_ordinal, "array")) return false; - if (lhs->value == get(Type_ordinal, "address")) return false; - return size_of(rhs) == size_of(lhs); - } + if (!rhs) return lhs->value == 0; if (lhs->value != rhs->value) return false; - return types_match(lhs->left, rhs->left) && types_match(lhs->right, rhs->right); + return types_strictly_match(lhs->left, rhs->left) && types_strictly_match(lhs->right, rhs->right); } bool is_raw(const reagent& r) { diff --git a/035call_ingredient.cc b/035call_ingredient.cc index 89d6507d..b949230d 100644 --- a/035call_ingredient.cc +++ b/035call_ingredient.cc @@ -62,6 +62,7 @@ case NEXT_INGREDIENT: { else if (!types_match(product, current_call().ingredients.at(current_call().next_ingredient_to_process))) { raise_error << maybe(current_recipe_name()) << "wrong type for ingredient " << product.original_string << '\n' << end(); + // End next-ingredient Type Mismatch Error } products.push_back( current_call().ingredient_atoms.at(current_call().next_ingredient_to_process)); diff --git a/048check_type_by_name.cc b/048check_type_by_name.cc index 1d4e7dc1..ea4bcba2 100644 --- a/048check_type_by_name.cc +++ b/048check_type_by_name.cc @@ -57,7 +57,7 @@ void check_type(map& type, map& type_n if (!contains_key(type_name, x.name)) { type_name[x.name] = x.properties.at(0).second; } - if (!types_match(type[x.name], x.type)) + if (!types_strictly_match(type[x.name], x.type)) raise_error << maybe(get(Recipe, r).name) << x.name << " used with multiple types\n" << end(); } diff --git a/057static_dispatch.cc b/057static_dispatch.cc index a333838c..6613012a 100644 --- a/057static_dispatch.cc +++ b/057static_dispatch.cc @@ -140,10 +140,13 @@ void resolve_ambiguous_calls(recipe_ordinal r) { void replace_best_variant(instruction& inst, const recipe& caller_recipe) { trace(9992, "transform") << "instruction " << inst.name << end(); vector& variants = get(Recipe_variants, inst.name); +//? trace(9992, "transform") << "checking base: " << get(Recipe_ordinal, inst.name) << end(); long long int best_score = variant_score(inst, get(Recipe_ordinal, inst.name)); + trace(9992, "transform") << "score for base: " << best_score << end(); for (long long int i = 0; i < SIZE(variants); ++i) { +//? trace(9992, "transform") << "checking variant " << i << ": " << variants.at(i) << end(); long long int current_score = variant_score(inst, variants.at(i)); - trace(9992, "transform") << "checking variant " << i << ": " << current_score << end(); + trace(9992, "transform") << "score for variant " << i << ": " << current_score << end(); if (current_score > best_score) { inst.name = get(Recipe, variants.at(i)).name; best_score = current_score; @@ -153,7 +156,7 @@ void replace_best_variant(instruction& inst, const recipe& caller_recipe) { } long long int variant_score(const instruction& inst, recipe_ordinal variant) { - long long int result = 100; + long long int result = 1000; if (variant == -1) return -1; // ghost from a previous test //? cerr << "variant score: " << inst.to_string() << '\n'; if (!contains_key(Recipe, variant)) { @@ -177,11 +180,16 @@ long long int variant_score(const instruction& inst, recipe_ordinal variant) { trace(9993, "transform") << "strict match: ingredient " << i << end(); //? cerr << "strict match: ingredient " << i << '\n'; } + else if (boolean_matches_literal(header_ingredients.at(i), inst.ingredients.at(i))) { + // slight penalty for coercing literal to boolean (prefer direct conversion to number if possible) + trace(9993, "transform") << "boolean matches literal: ingredient " << i << end(); + result--; + } else { - // slight penalty for modifying type + // slightly larger penalty for modifying type in other ways trace(9993, "transform") << "non-strict match: ingredient " << i << end(); //? cerr << "non-strict match: ingredient " << i << '\n'; - result--; + result-=10; } } //? cerr << "=== done checking ingredients\n"; @@ -201,11 +209,16 @@ long long int variant_score(const instruction& inst, recipe_ordinal variant) { trace(9993, "transform") << "strict match: product " << i << end(); //? cerr << "strict match: product " << i << '\n'; } + else if (boolean_matches_literal(header_products.at(i), inst.products.at(i))) { + // slight penalty for coercing literal to boolean (prefer direct conversion to number if possible) + trace(9993, "transform") << "boolean matches literal: product " << i << end(); + result--; + } else { - // slight penalty for modifying type + // slightly larger penalty for modifying type in other ways trace(9993, "transform") << "non-strict match: product " << i << end(); //? cerr << "non-strict match: product " << i << '\n'; - result--; + result-=10; } } // the greater the number of unused ingredients/products, the lower the score @@ -279,3 +292,54 @@ recipe foo x:number -> y:number [ ] +error: foo: wrong type for ingredient x:number -mem: storing 34 in location 1 + +:(scenario static_dispatch_dispatches_literal_to_boolean_before_character) +recipe main [ + 1:number/raw <- foo 0 # valid literal for boolean +] +recipe foo x:character -> y:number [ + local-scope + load-ingredients + reply 34 +] +recipe foo x:boolean -> y:number [ + local-scope + load-ingredients + reply 35 +] +# boolean variant is preferred ++mem: storing 35 in location 1 + +:(scenario static_dispatch_dispatches_literal_to_character_when_out_of_boolean_range) +recipe main [ + 1:number/raw <- foo 97 # not a valid literal for boolean +] +recipe foo x:character -> y:number [ + local-scope + load-ingredients + reply 34 +] +recipe foo x:boolean -> y:number [ + local-scope + load-ingredients + reply 35 +] +# character variant is preferred ++mem: storing 34 in location 1 + +:(scenario static_dispatch_dispatches_literal_to_number_if_at_all_possible) +recipe main [ + 1:number/raw <- foo 97 +] +recipe foo x:character -> y:number [ + local-scope + load-ingredients + reply 34 +] +recipe foo x:number -> y:number [ + local-scope + load-ingredients + reply 35 +] +# number variant is preferred ++mem: storing 35 in location 1 diff --git a/999spaces.cc b/999spaces.cc index 2ab83f87..8c0b2a60 100644 --- a/999spaces.cc +++ b/999spaces.cc @@ -69,3 +69,16 @@ assert(Max_callstack_depth == 9989); //: 56 check reply instructions against header //: end checks //: end transforms + +//:: Summary of type-checking in different phases +//: when dispatching instructions we accept first recipe that: +//: strictly matches all types +//: maps literal 0 or literal 1 to boolean for some ingredients +//: performs some other acceptable type conversion +//: literal 0 -> address +//: literal -> character +//: when checking instructions we ensure that types match, and that literals map to some scalar +//: (address can only map to literal 0) +//: (boolean can only map to literal 0 or literal 1) +//: (but conditionals can take any scalar) +//: at runtime we perform no checks -- cgit 1.4.1-2-gfad0