2607 - resolve some edge cases in static dispatch

This commit is contained in:
Kartik K. Agaram 2015-11-29 11:43:25 -08:00
parent afb467ea02
commit 691b529e53
5 changed files with 101 additions and 16 deletions

View File

@ -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) {

View File

@ -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));

View File

@ -57,7 +57,7 @@ void check_type(map<string, type_tree*>& type, map<string, string_tree*>& 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();
}

View File

@ -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<recipe_ordinal>& 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

View File

@ -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