1 //: Some simple sanity checks for types, and also attempts to guess them where
  2 //: they aren't provided.
  3 //:
  4 //: You still have to provide the full type the first time you mention a
  5 //: variable in a recipe. You have to explicitly name :offset and :variant
  6 //: every single time. You can't use the same name with multiple types in a
  7 //: single recipe.
  8 
  9 :(scenario transform_fails_on_reusing_name_with_different_type)
 10 % Hide_errors = true;
 11 def main [
 12   x:num <- copy 1
 13   x:bool <- copy 1
 14 ]
 15 +error: main: 'x' used with multiple types
 16 
 17 :(after "Transform.push_back(expand_type_abbreviations)")
 18 Transform.push_back(check_or_set_types_by_name);  // idempotent
 19 
 20 :(code)
 21 void check_or_set_types_by_name(const recipe_ordinal r) {
 22   trace(9991, "transform") << "--- deduce types for recipe " << get(Recipe, r).name << end();
 23   recipe& caller = get(Recipe, r);
 24   set<reagent> known;
 25   for (int i = 0;  i < SIZE(caller.steps);  ++i) {
 26     instruction& inst = caller.steps.at(i);
 27     for (int in = 0;  in < SIZE(inst.ingredients);  ++in) {
 28       deduce_missing_type(known, inst.ingredients.at(in), caller);
 29       check_type(known, inst.ingredients.at(in), caller);
 30     }
 31     for (int out = 0;  out < SIZE(inst.products);  ++out) {
 32       deduce_missing_type(known, inst.products.at(out), caller);
 33       check_type(known, inst.products.at(out), caller);
 34     }
 35   }
 36 }
 37 
 38 void deduce_missing_type(set<reagent>& known, reagent& x, const recipe& caller) {
 39   // Deduce Missing Type(x, caller)
 40   if (x.type) return;
 41   if (is_jump_target(x.name)) {
 42     x.type = new type_tree("label");
 43     return;
 44   }
 45   if (known.find(x) == known.end()) return;
 46   const reagent& exemplar = *known.find(x);
 47   x.type = new type_tree(*exemplar.type);
 48   trace(9992, "transform") << x.name << " <= " << names_to_string(x.type) << end();
 49   // spaces are special; their type includes their /names property
 50   if (is_mu_space(x) && !has_property(x, "names")) {
 51     if (!has_property(exemplar, "names")) {
 52       raise << maybe(caller.name) << "missing /names property for space variable '" << exemplar.name << "'\n" << end();
 53       return;
 54     }
 55     x.properties.push_back(pair<string, string_tree*>("names", new string_tree(*property(exemplar, "names"))));
 56   }
 57 }
 58 
 59 void check_type(set<reagent>& known, const reagent& x, const recipe& caller) {
 60   if (is_literal(x)) return;
 61   if (is_integer(x.name)) return;  // if you use raw locations you're probably doing something unsafe
 62   if (!x.type) return;  // might get filled in by other logic later
 63   if (is_jump_target(x.name)) {
 64     if (!x.type->atom || x.type->name != "label")
 65       raise << maybe(caller.name) << "non-label '" << x.name << "' must begin with a letter\n" << end();
 66     return;
 67   }
 68   if (known.find(x) == known.end()) {
 69     trace(9992, "transform") << x.name << " => " << names_to_string(x.type) << end();
 70     known.insert(x);
 71   }
 72   if (!types_strictly_match(known.find(x)->type, x.type)) {
 73     raise << maybe(caller.name) << "'" << x.name << "' used with multiple types\n" << end();
 74     return;
 75   }
 76   if (is_mu_array(x)) {
 77     if (!x.type->right) {
 78       raise << maybe(caller.name) << "'" << x.name << ": can't be just an array. What is it an array of?\n" << end();
 79       return;
 80     }
 81     if (!x.type->right->right) {
 82       raise << caller.name << " can't determine the size of array variable '" << x.name << "'. Either allocate it separately and make the type of '" << x.name << "' an address, or specify the length of the array in the type of '" << x.name << "'.\n" << end();
 83       return;
 84     }
 85   }
 86 }
 87 
 88 :(scenario transform_fills_in_missing_types)
 89 def main [
 90   x:num <- copy 1
 91   y:num <- add x, 1
 92 ]
 93 # x is in location 1, y in location 2
 94 +mem: storing 2 in location 2
 95 
 96 :(scenario transform_fills_in_missing_types_in_product)
 97 def main [
 98   x:num <- copy 1
 99   x <- copy 2
100 ]
101 # x is in location 1
102 +mem: storing 2 in location 1
103 
104 :(scenario transform_fills_in_missing_types_in_product_and_ingredient)
105 def main [
106   x:num <- copy 1
107   x <- add x, 1
108 ]
109 # x is in location 1
110 +mem: storing 2 in location 1
111 
112 :(scenario transform_fills_in_missing_label_type)
113 def main [
114   jump +target
115   1:num <- copy 0
116   +target
117 ]
118 -mem: storing 0 in location 1
119 
120 :(scenario transform_fails_on_missing_types_in_first_mention)
121 % Hide_errors = true;
122 def main [
123   x <- copy 1
124   x:num <- copy 2
125 ]
126 +error: main: missing type for 'x' in 'x <- copy 1'
127 
128 :(scenario transform_fails_on_wrong_type_for_label)
129 % Hide_errors = true;
130 def main [
131   +foo:num <- copy 34
132 ]
133 +error: main: non-label '+foo' must begin with a letter
134 
135 :(scenario typo_in_address_type_fails)
136 % Hide_errors = true;
137 def main [
138   y:&:charcter <- new character:type
139   *y <- copy 67
140 ]
141 +error: main: unknown type charcter in 'y:&:charcter <- new character:type'
142 
143 :(scenario array_type_without_size_fails)
144 % Hide_errors = true;
145 def main [
146   x:@:num <- merge 2, 12, 13
147 ]
148 +error: main can't determine the size of array variable 'x'. Either allocate it separately and make the type of 'x' an address, or specify the length of the array in the type of 'x'.
149 
150 :(scenarios transform)
151 :(scenario transform_checks_types_of_identical_reagents_in_multiple_spaces)
152 def foo [  # dummy
153 ]
154 def main [
155   local-scope
156   0:space/names:foo <- copy 0  # specify surrounding space
157   x:bool <- copy 1/true
158   x:num/space:1 <- copy 34
159   x/space:1 <- copy 35
160 ]
161 $error: 0
162 
163 :(scenario transform_handles_empty_reagents)
164 % Hide_errors = true;
165 def main [
166   add *
167 ]
168 +error: illegal name '*'
169 # no crash