summary refs log tree commit diff stats
path: root/tests/misc/tgenconstraints.nim
blob: 829da5173bc04f977ee7034b1b2f93d64a4b4bf7 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
discard """
  errormsg: "cannot instantiate T2"
  file: "tgenconstraints.nim"
  line: 25
  disabled: true
"""

type
  T1[T: int|string] = object
    x: T

  T2[T: Ordinal] = object
    x: T

var x1: T1[int]
var x2: T1[string]
var x3: T2[int]

proc foo[T](x: T): T2[T] {.discardable.} =
  var o: T1[T]

foo(10)

# XXX: allow type intersections in situation like this
proc bar(x: int|TNumber): T1[type(x)] {.discardable.} =
  when type(x) is TNumber:
    var o: T2[type(x)]

bar "test"
bar 100
bar 1.1
checking in a separate part complicates things, because the values //: of variables in memory and the processor (current_recipe_name, //: current_instruction) aren't available at checking time. If I had a more //: sophisticated layer system I'd introduce the simpler version first and //: transform it in a separate layer or set of layers. :(after "Transform.push_back(update_instruction_operations)") Transform.push_back(check_instruction); // idempotent :(code) void check_instruction(const recipe_ordinal r) { trace(9991, "transform") << "--- perform checks for recipe " << get(Recipe, r).name << end(); //? cerr << "--- perform checks for recipe " << get(Recipe, r).name << '\n'; map<string, vector<type_ordinal> > metadata; for (long long int i = 0; i < SIZE(get(Recipe, r).steps); ++i) { instruction& inst = get(Recipe, r).steps.at(i); if (inst.is_label) continue; switch (inst.operation) { // Primitive Recipe Checks case COPY: { if (SIZE(inst.products) != SIZE(inst.ingredients)) { raise_error << "ingredients and products should match in '" << inst.to_string() << "'\n" << end(); break; } for (long long int i = 0; i < SIZE(inst.ingredients); ++i) { if (!types_coercible(inst.products.at(i), inst.ingredients.at(i))) { raise_error << maybe(get(Recipe, r).name) << "can't copy " << inst.ingredients.at(i).original_string << " to " << inst.products.at(i).original_string << "; types don't match\n" << end(); goto finish_checking_instruction; } } break; } // End Primitive Recipe Checks default: { // Defined Recipe Checks // End Defined Recipe Checks } } finish_checking_instruction:; } } :(scenario copy_checks_reagent_count) % Hide_errors = true; recipe main [ 1:number <- copy 34, 35 ] +error: ingredients and products should match in '1:number <- copy 34, 35' :(scenario write_scalar_to_array_disallowed) % Hide_errors = true; recipe main [ 1:array:number <- copy 34 ] +error: main: can't copy 34 to 1:array:number; types don't match :(scenario write_scalar_to_array_disallowed_2) % Hide_errors = true; recipe main [ 1:number, 2:array:number <- copy 34, 35 ] +error: main: can't copy 35 to 2:array:number; types don't match :(scenario write_scalar_to_address_disallowed) % Hide_errors = true; recipe main [ 1:address:number <- copy 34 ] +error: main: can't copy 34 to 1:address:number; types don't match :(scenario write_address_to_number_allowed) % Hide_errors = true; recipe main [ 1:address:number <- copy 12/unsafe 2:number <- copy 1:address:number ] +mem: storing 12 in location 2 $error: 0 :(code) // types_match with some special-cases 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; // End types_coercible Special-cases return false; } bool types_match(const reagent& lhs, const reagent& rhs) { if (!is_unsafe(rhs)) { if (is_mu_address(lhs) && is_literal(rhs)) return rhs.name == "0"; if (is_literal(rhs)) return valid_type_for_literal(lhs, rhs) && size_of(rhs) == size_of(lhs); } return types_strictly_match(lhs, rhs); } // copy arguments because later layers will want to make changes to them // without perturbing the caller bool types_strictly_match(reagent lhs, reagent rhs) { // '_' never raises type error if (is_dummy(lhs)) 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; if (!lhs.type) return !rhs.type; return types_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) { 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 (lhs->value != rhs->value) return false; return types_match(lhs->left, rhs->left) && types_match(lhs->right, rhs->right); } bool valid_type_for_literal(const reagent& lhs, const reagent& literal_rhs) { if (is_mu_array(lhs)) return false; // End valid_type_for_literal Special-cases // allow writing 0 to any address if (is_mu_address(lhs)) return literal_rhs.name == "0"; return true; } bool is_raw(const reagent& r) { return has_property(r, "raw"); } bool is_unsafe(const reagent& r) { return has_property(r, "unsafe"); } bool is_mu_array(reagent r) { if (!r.type) return false; if (is_literal(r)) return false; return r.type->value == get(Type_ordinal, "array"); } bool is_mu_address(reagent r) { if (!r.type) return false; if (is_literal(r)) return false; return r.type->value == get(Type_ordinal, "address"); } bool is_mu_number(reagent r) { if (!r.type) return false; if (is_literal(r)) return r.properties.at(0).second->value == "literal-number" || r.properties.at(0).second->value == "literal"; if (r.type->value == get(Type_ordinal, "character")) return true; // permit arithmetic on unicode code points return r.type->value == get(Type_ordinal, "number"); } bool is_mu_scalar(reagent r) { if (!r.type) return false; if (is_literal(r)) return !r.properties.at(0).second || r.properties.at(0).second->value != "literal-string"; if (is_mu_array(r)) return false; return size_of(r) == 1; }