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 '" << inst.original_string << "'\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_boolean_to_number_allowed)
83 def main [
84 1:bool <- copy 1/true
85 2:num <- copy 1:bool
86 ]
87 +mem: storing 1 in location 2
88 $error: 0
89
90 :(scenario write_number_to_boolean_allowed)
91 def main [
92 1:num <- copy 34
93 2:bool <- copy 1:num
94 ]
95 +mem: storing 34 in location 2
96 $error: 0
97
98 :(code)
99
100 bool types_coercible(const reagent& to, const reagent& from) {
101 if (types_match(to, from)) return true;
102 if (is_mu_address(from) && is_mu_number(to)) return true;
103 if (is_mu_boolean(from) && is_mu_number(to)) return true;
104 if (is_mu_number(from) && is_mu_boolean(to)) return true;
105
106 return false;
107 }
108
109 bool types_match(const reagent& to, const reagent& from) {
110
111
112 if (is_unsafe(from)) return true;
113 if (is_literal(from)) {
114 ¦ if (is_mu_array(to)) return false;
115 ¦
116 ¦
117 ¦ if (is_mu_address(to)) return from.name == "0";
118 ¦ if (!to.type) return false;
119 ¦ if (to.type->atom && to.type->value == get(Type_ordinal, "boolean"))
120 ¦ ¦ return from.name == "0" || from.name == "1";
121 ¦ return size_of(to) == 1;
122 }
123 return types_strictly_match(to, from);
124 }
125
126
127 bool types_strictly_match(reagent to, reagent from) {
128
129 if (to.type == NULL) return false;
130 if (is_literal(from) && to.type->value == get(Type_ordinal, "number")) return true;
131
132
133 if (is_unsafe(from)) return true;
134
135 if (is_dummy(to)) return true;
136 if (!to.type) return !from.type;
137 return types_strictly_match(to.type, from.type);
138 }
139
140 bool types_strictly_match(const type_tree* to, const type_tree* from) {
141 if (from == to) return true;
142 if (!to) return false;
143 if (!from) return to->atom && to->value == 0;
144 if (from->atom != to->atom) return false;
145 if (from->atom) {
146 ¦ if (from->value == -1) return from->name == to->name;
147 ¦ return from->value == to->value;
148 }
149 return types_strictly_match(to->left, from->left) && types_strictly_match(to->right, from->right);
150 }
151
152 void test_unknown_type_does_not_match_unknown_type() {
153 reagent a("a:foo");
154 reagent b("b:bar");
155 CHECK(!types_strictly_match(a, b));
156 }
157
158 void test_unknown_type_matches_itself() {
159 reagent a("a:foo");
160 reagent b("b:foo");
161 CHECK(types_strictly_match(a, b));
162 }
163
164
165
166 bool is_unsafe(const reagent& r) {
167 return has_property(r, "unsafe");
168 }
169
170 bool is_mu_array(reagent r) {
171
172 return is_mu_array(r.type);
173 }
174 bool is_mu_array(const type_tree* type) {
175 if (!type) return false;
176 if (is_literal(type)) return false;
177 if (type->atom) return false;
178 if (!type->left->atom) {
179 ¦ raise << "invalid type " << to_string(type) << '\n' << end();
180 ¦ return false;
181 }
182 return type->left->value == get(Type_ordinal, "array");
183 }
184
185 bool is_mu_address(reagent r) {
186
187 return is_mu_address(r.type);
188 }
189 bool is_mu_address(const type_tree* type) {
190 if (!type) return false;
191 if (is_literal(type)) return false;
192 if (type->atom) return false;
193 if (!type->left->atom) {
194 ¦ raise << "invalid type " << to_string(type) << '\n' << end();
195 ¦ return false;
196 }
197 return type->left->value == get(Type_ordinal, "address");
198 }
199
200 bool is_mu_boolean(reagent r) {
201
202 if (!r.type) return false;
203 if (is_literal(r)) return false;
204 if (!r.type->atom) return false;
205 return r.type->value == get(Type_ordinal, "boolean");
206 }
207
208 bool is_mu_number(reagent r) {
209
210 if (!r.type) return false;
211 if (!r.type->atom) return false;
212 if (is_literal(r)) {
213 ¦ return r.type->name == "literal-fractional-number"
214 ¦ ¦ ¦ || r.type->name == "literal";
215 }
216 if (r.type->value == get(Type_ordinal, "character")) return true;
217 return r.type->value == get(Type_ordinal, "number");
218 }
219
220 bool is_mu_character(reagent r) {
221
222 return is_mu_character(r.type);
223 }
224 bool is_mu_character(const type_tree* type) {
225 if (!type) return false;
226 if (!type->atom) return false;
227 if (is_literal(type)) return false;
228 return type->value == get(Type_ordinal, "character");
229 }
230
231 bool is_mu_scalar(reagent r) {
232 return is_mu_scalar(r.type);
233 }
234 bool is_mu_scalar(const type_tree* type) {
235 if (!type) return false;
236 if (is_mu_address(type)) return true;
237 if (!type->atom) return false;
238 if (is_literal(type))
239 ¦ return type->name != "literal-string";
240 return size_of(type) == 1;
241 }