1
2
3
4
5
6
7
8
9
10
11 :(before "End Checks")
12 Transform.push_back(check_instruction);
13
14 :(code)
15 void check_instruction(const recipe_ordinal r) {
16 trace(9991, "transform") << "--- perform checks for recipe " << get(Recipe, r).name << end();
17 map<string, vector<type_ordinal> > metadata;
18 for (int i = 0; i < SIZE(get(Recipe, r).steps); ++i) {
19 instruction& inst = get(Recipe, r).steps.at(i);
20 if (inst.is_label) continue;
21 switch (inst.operation) {
22
23 case COPY: {
24 if (SIZE(inst.products) > SIZE(inst.ingredients)) {
25 raise << maybe(get(Recipe, r).name) << "too many products in '" << to_original_string(inst) << "'\n" << end();
26 break;
27 }
28 for (int i = 0; i < SIZE(inst.products); ++i) {
29 if (!types_coercible(inst.products.at(i), inst.ingredients.at(i))) {
30 raise << 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();
31 goto finish_checking_instruction;
32 }
33 }
34 break;
35 }
36
37 default: {
38
39
40 }
41 }
42 finish_checking_instruction:;
43 }
44 }
45
46 :(scenario copy_checks_reagent_count)
47 % Hide_errors = true;
48 def main [
49 1:num, 2:num <- copy 34
50 ]
51 +error: main: too many products in '1:num, 2:num <- copy 34'
52
53 :(scenario write_scalar_to_array_disallowed)
54 % Hide_errors = true;
55 def main [
56 1:array:num <- copy 34
57 ]
58 +error: main: can't copy '34' to '1:array:num'; types don't match
59
60 :(scenario write_scalar_to_array_disallowed_2)
61 % Hide_errors = true;
62 def main [
63 1:num, 2:array:num <- copy 34, 35
64 ]
65 +error: main: can't copy '35' to '2:array:num'; types don't match
66
67 :(scenario write_scalar_to_address_disallowed)
68 % Hide_errors = true;
69 def main [
70 1:address:num <- copy 34
71 ]
72 +error: main: can't copy '34' to '1:address:num'; types don't match
73
74 :(scenario write_address_to_number_allowed)
75 def main [
76 1:address:num <- copy 12/unsafe
77 2:num <- copy 1:address:num
78 ]
79 +mem: storing 12 in location 2
80 $error: 0
81
82 :(scenario write_address_to_character_disallowed)
83 % Hide_errors = true;
84 def main [
85 1:address:num <- copy 12/unsafe
86 2:char <- copy 1:address:num
87 ]
88 +error: main: can't copy '1:address:num' to '2:char'; types don't match
89
90 :(scenario write_number_to_character_allowed)
91 def main [
92 1:num <- copy 97
93 2:char <- copy 1:num
94 ]
95 $error: 0
96
97 :(scenario write_boolean_to_number_allowed)
98 def main [
99 1:bool <- copy 1/true
100 2:num <- copy 1:bool
101 ]
102 +mem: storing 1 in location 2
103 $error: 0
104
105 :(scenario write_number_to_boolean_disallowed)
106 % Hide_errors = true;
107 def main [
108 1:num <- copy 34
109 2:bool <- copy 1:num
110 ]
111 +error: main: can't copy '1:num' to '2:bool'; types don't match
112
113 :(code)
114
115 bool types_coercible(const reagent& to, const reagent& from) {
116 if (types_match(to, from)) return true;
117 if (is_mu_address(from) && is_real_mu_number(to)) return true;
118 if (is_mu_boolean(from) && is_real_mu_number(to)) return true;
119 if (is_real_mu_number(from) && is_mu_character(to)) return true;
120
121 return false;
122 }
123
124 bool types_match(const reagent& to, const reagent& from) {
125
126
127 if (is_unsafe(from)) return true;
128 if (is_literal(from)) {
129 if (is_mu_array(to)) return false;
130
131
132 if (is_mu_address(to)) return from.name == "0";
133 if (!to.type) return false;
134 if (is_mu_boolean(to)) return from.name == "0" || from.name == "1";
135 return size_of(to) == 1;
136 }
137 return types_strictly_match(to, from);
138 }
139
140
141 bool types_strictly_match(reagent to, reagent from) {
142
143 if (to.type == NULL) return false;
144 if (is_literal(from) && to.type->value == Number_type_ordinal) return true;
145
146
147 if (is_unsafe(from)) return true;
148
149 if (is_dummy(to)) return true;
150 if (!to.type) return !from.type;
151 return types_strictly_match(to.type, from.type);
152 }
153
154 bool types_strictly_match(const type_tree* to, const type_tree* from) {
155 if (from == to) return true;
156 if (!to) return false;
157 if (!from) return to->atom && to->value == 0;
158 if (from->atom != to->atom) return false;
159 if (from->atom) {
160 if (from->value == -1) return from->name == to->name;
161 return from->value == to->value;
162 }
163 if (types_strictly_match(to->left, from->left) && types_strictly_match(to->right, from->right))
164 return true;
165
166 if (to->right == NULL && types_strictly_match(to->left, from)) return true;
167 if (from->right == NULL && types_strictly_match(to, from->left)) return true;
168 return false;
169 }
170
171 void test_unknown_type_does_not_match_unknown_type() {
172 reagent a("a:foo");
173 reagent b("b:bar");
174 CHECK(!types_strictly_match(a, b));
175 }
176
177 void test_unknown_type_matches_itself() {
178 reagent a("a:foo");
179 reagent b("b:foo");
180 CHECK(types_strictly_match(a, b));
181 }
182
183 void test_type_abbreviations_match_raw_types() {
184 put(Type_abbreviations, "text", new_type_tree("address:array:character"));
185
186 reagent a("a:address:buffer:text");
187 expand_type_abbreviations(a.type);
188
189 reagent b("b:address:buffer:address:array:character");
190 CHECK(types_strictly_match(a, b));
191 delete Type_abbreviations["text"];
192 put(Type_abbreviations, "text", NULL);
193 }
194
195
196
197 bool is_unsafe(const reagent& r) {
198 return has_property(r, "unsafe");
199 }
200
201 bool is_mu_array(reagent r) {
202
203 return is_mu_array(r.type);
204 }
205 bool is_mu_array(const type_tree* type) {
206 if (!type) return false;
207 if (is_literal(type)) return false;
208 if (type->atom) return false;
209 if (!type->left->atom) {
210 raise << "invalid type " << to_string(type) << '\n' << end();
211 return false;
212 }
213 return type->left->value == Array_type_ordinal;
214 }
215
216 bool is_mu_address(reagent r) {
217
218 return is_mu_address(r.type);
219 }
220 bool is_mu_address(const type_tree* type) {
221 if (!type) return false;
222 if (is_literal(type)) return false;
223 if (type->atom) return false;
224 if (!type->left->atom) {
225 raise << "invalid type " << to_string(type) << '\n' << end();
226 return false;
227 }
228 return type->left->value == Address_type_ordinal;
229 }
230
231 bool is_mu_boolean(reagent r) {
232
233 if (!r.type) return false;
234 if (is_literal(r)) return false;
235 if (!r.type->atom) return false;
236 return r.type->value == Boolean_type_ordinal;
237 }
238
239 bool is_mu_number(reagent r) {
240 if (is_mu_character(r.type)) return true;
241 return is_real_mu_number(r);
242 }
243
244 bool is_real_mu_number(reagent r) {
245
246 if (!r.type) return false;
247 if (!r.type->atom) return false;
248 if (is_literal(r)) {
249 return r.type->name == "literal-fractional-number"
250 || r.type->name == "literal";
251 }
252 return r.type->value == Number_type_ordinal;
253 }
254
255 bool is_mu_character(reagent r) {
256
257 return is_mu_character(r.type);
258 }
259 bool is_mu_character(const type_tree* type) {
260 if (!type) return false;
261 if (!type->atom) return false;
262 if (is_literal(type)) return false;
263 return type->value == Character_type_ordinal;
264 }
265
266 bool is_mu_scalar(reagent r) {
267 return is_mu_scalar(r.type);
268 }
269 bool is_mu_scalar(const type_tree* type) {
270 if (!type) return false;
271 if (is_mu_address(type)) return true;
272 if (!type->atom) return false;
273 if (is_literal(type))
274 return type->name != "literal-string";
275 return size_of(type) == 1;
276 }