https://github.com/akkartik/mu/blob/master/021check_instruction.cc
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:&:num <- copy 34
71 ]
72 +error: main: can't copy '34' to '1:&:num'; types don't match
73
74 :(scenario write_address_to_character_disallowed)
75 % Hide_errors = true;
76 def main [
77 1:&:num <- copy 12/unsafe
78 2:char <- copy 1:&:num
79 ]
80 +error: main: can't copy '1:&:num' to '2:char'; types don't match
81
82 :(scenario write_number_to_character_allowed)
83 def main [
84 1:num <- copy 97
85 2:char <- copy 1:num
86 ]
87 $error: 0
88
89 :(code)
90
91 bool types_coercible(reagent to, reagent from) {
92
93 if (types_match_sub(to, from)) return true;
94 if (is_real_mu_number(from) && is_mu_character(to)) return true;
95
96 return false;
97 }
98
99 bool types_match_sub(const reagent& to, const reagent& from) {
100
101
102 if (is_unsafe(from)) return true;
103 if (is_literal(from)) {
104 if (is_mu_array(to)) return false;
105
106 if (!to.type) return false;
107
108 if (is_mu_address(to)) return from.name == "null";
109 return size_of(to) == 1;
110 }
111 return types_strictly_match_sub(to, from);
112 }
113
114 bool types_match(reagent to, reagent from) {
115
116 return types_match_sub(to, from);
117 }
118
119
120 bool types_strictly_match_sub(const reagent& to, const reagent& from) {
121 if (to.type == NULL) return false;
122 if (is_literal(from) && to.type->value == Number_type_ordinal) return true;
123
124
125 if (is_unsafe(from)) return true;
126
127 if (is_dummy(to)) return true;
128 if (!to.type) return !from.type;
129 return types_strictly_match(to.type, from.type);
130 }
131
132 bool types_strictly_match(reagent to, reagent from) {
133
134 return types_strictly_match_sub(to, from);
135 }
136
137 bool types_strictly_match(const type_tree* to, const type_tree* from) {
138 if (from == to) return true;
139 if (!to) return false;
140 if (!from) return to->atom && to->value == 0;
141 if (from->atom != to->atom) return false;
142 if (from->atom) {
143 if (from->value == -1) return from->name == to->name;
144 return from->value == to->value;
145 }
146 if (types_strictly_match(to->left, from->left) && types_strictly_match(to->right, from->right))
147 return true;
148
149 if (to->right == NULL && types_strictly_match(to->left, from)) return true;
150 if (from->right == NULL && types_strictly_match(to, from->left)) return true;
151 return false;
152 }
153
154 void test_unknown_type_does_not_match_unknown_type() {
155 reagent a("a:foo");
156 reagent b("b:bar");
157 CHECK(!types_strictly_match(a, b));
158 }
159
160 void test_unknown_type_matches_itself() {
161 reagent a("a:foo");
162 reagent b("b:foo");
163 CHECK(types_strictly_match(a, b));
164 }
165
166 void test_type_abbreviations_match_raw_types() {
167 put(Type_abbreviations, "text", new_type_tree("address:array:character"));
168
169 reagent a("a:address:buffer:text");
170 expand_type_abbreviations(a.type);
171
172 reagent b("b:address:buffer:address:array:character");
173 CHECK(types_strictly_match(a, b));
174 delete Type_abbreviations["text"];
175 put(Type_abbreviations, "text", NULL);
176 }
177
178
179
180 bool is_unsafe(const reagent& r) {
181 return has_property(r, "unsafe");
182 }
183
184 bool is_mu_array(reagent r) {
185
186 return is_mu_array(r.type);
187 }
188 bool is_mu_array(const type_tree* type) {
189 if (!type) return false;
190 if (is_literal(type)) return false;
191 if (type->atom) return false;
192 if (!type->left->atom) {
193 raise << "invalid type " << to_string(type) << '\n' << end();
194 return false;
195 }
196 return type->left->value == Array_type_ordinal;
197 }
198
199 bool is_mu_boolean(reagent r) {
200
201 if (!r.type) return false;
202 if (is_literal(r)) return false;
203 if (!r.type->atom) return false;
204 return r.type->value == Boolean_type_ordinal;
205 }
206
207 bool is_mu_number(reagent r) {
208 if (is_mu_character(r.type)) return true;
209 return is_real_mu_number(r);
210 }
211
212 bool is_real_mu_number(reagent r) {
213
214 if (!r.type) return false;
215 if (!r.type->atom) return false;
216 if (is_literal(r)) {
217 return r.type->name == "literal-fractional-number"
218 || r.type->name == "literal";
219 }
220 return r.type->value == Number_type_ordinal;
221 }
222
223 bool is_mu_character(reagent r) {
224
225 return is_mu_character(r.type);
226 }
227 bool is_mu_character(const type_tree* type) {
228 if (!type) return false;
229 if (!type->atom) return false;
230 if (is_literal(type)) return false;
231 return type->value == Character_type_ordinal;
232 }