blob: 4e7d8c7a7f3f0d07e3145842b800ecfe64e8c055 [file] [log] [blame]
Ben Olmsteadc0d77842019-07-31 17:34:05 -07001# Copyright 2019 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15"""Tests for attribute_checker.py."""
16
17import unittest
reventlov6731fc42019-10-03 15:23:13 -070018from compiler.front_end import attribute_checker
19from compiler.front_end import glue
reventlov6731fc42019-10-03 15:23:13 -070020from compiler.util import error
Eric Rahm12c5e842024-03-22 09:37:24 -070021from compiler.util import ir_data
reventlov6731fc42019-10-03 15:23:13 -070022from compiler.util import ir_util
Jason Graffius91910772023-05-18 12:43:52 -040023from compiler.util import test_util
Ben Olmsteadc0d77842019-07-31 17:34:05 -070024
25# These are not shared with attribute_checker.py because their values are part
26# of the contract with back ends.
27_BYTE_ORDER = "byte_order"
28_FIXED_SIZE = "fixed_size_in_bits"
reventlovc50913d2022-04-18 15:28:36 -070029_IS_SIGNED = "is_signed"
30_MAX_BITS = "maximum_bits"
Ben Olmsteadc0d77842019-07-31 17:34:05 -070031
32
33def _make_ir_from_emb(emb_text, name="m.emb"):
34 ir, unused_debug_info, errors = glue.parse_emboss_file(
35 name,
36 test_util.dict_file_reader({name: emb_text}),
37 stop_before_step="normalize_and_verify")
38 assert not errors
39 return ir
40
41
42class NormalizeIrTest(unittest.TestCase):
43
44 def test_rejects_may_be_used_as_integer(self):
45 enum_ir = _make_ir_from_emb("enum Foo:\n"
46 " [may_be_used_as_integer: false]\n"
47 " VALUE = 1\n")
48 enum_type_ir = enum_ir.module[0].type[0]
49 self.assertEqual([[
50 error.error(
51 "m.emb", enum_type_ir.attribute[0].name.source_location,
52 "Unknown attribute 'may_be_used_as_integer' on enum 'Foo'.")
53 ]], attribute_checker.normalize_and_verify(enum_ir))
54
55 def test_adds_fixed_size_attribute_to_struct(self):
56 # field2 is intentionally after field3, in order to trigger certain code
57 # paths in attribute_checker.py.
58 struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
59 "struct Foo:\n"
60 " 0 [+2] UInt field1\n"
61 " 4 [+4] UInt field2\n"
62 " 2 [+2] UInt field3\n")
63 self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
64 size_attr = ir_util.get_attribute(struct_ir.module[0].type[0].attribute,
65 _FIXED_SIZE)
66 self.assertEqual(64, ir_util.constant_value(size_attr.expression))
67 self.assertEqual(struct_ir.module[0].type[0].source_location,
68 size_attr.source_location)
69
70 def test_adds_fixed_size_attribute_to_struct_with_virtual_field(self):
71 struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
72 "struct Foo:\n"
73 " 0 [+2] UInt field1\n"
74 " let field2 = field1\n"
75 " 2 [+2] UInt field3\n")
76 self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
77 size_attr = ir_util.get_attribute(struct_ir.module[0].type[0].attribute,
78 _FIXED_SIZE)
79 self.assertEqual(32, ir_util.constant_value(size_attr.expression))
80 self.assertEqual(struct_ir.module[0].type[0].source_location,
81 size_attr.source_location)
82
83 def test_adds_fixed_size_attribute_to_anonymous_bits(self):
84 struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
85 "struct Foo:\n"
86 " 0 [+4] bits:\n"
87 " 0 [+8] UInt field\n")
88 self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
89 size_attr = ir_util.get_attribute(struct_ir.module[0].type[0].attribute,
90 _FIXED_SIZE)
91 self.assertEqual(32, ir_util.constant_value(size_attr.expression))
92 bits_size_attr = ir_util.get_attribute(
93 struct_ir.module[0].type[0].subtype[0].attribute, _FIXED_SIZE)
94 self.assertEqual(8, ir_util.constant_value(bits_size_attr.expression))
95 self.assertEqual(struct_ir.module[0].type[0].source_location,
96 size_attr.source_location)
97
98 def test_does_not_add_fixed_size_attribute_to_variable_size_struct(self):
99 struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
100 "struct Foo:\n"
101 " 0 [+4] UInt n\n"
102 " 4 [+n] UInt:8[] payload\n")
103 self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
104 self.assertIsNone(ir_util.get_attribute(
105 struct_ir.module[0].type[0].attribute, _FIXED_SIZE))
106
107 def test_accepts_correct_fixed_size_and_size_attributes_on_struct(self):
108 struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
109 "struct Foo:\n"
110 " [fixed_size_in_bits: 64]\n"
111 " 0 [+2] UInt field1\n"
112 " 2 [+2] UInt field2\n"
113 " 4 [+4] UInt field3\n")
114 self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
115 size_attr = ir_util.get_attribute(struct_ir.module[0].type[0].attribute,
116 _FIXED_SIZE)
117 self.assertTrue(size_attr)
118 self.assertEqual(64, ir_util.constant_value(size_attr.expression))
119
120 def test_accepts_correct_size_attribute_on_struct(self):
121 struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
122 "struct Foo:\n"
123 " [fixed_size_in_bits: 64]\n"
124 " 0 [+2] UInt field1\n"
125 " 4 [+4] UInt field3\n")
126 self.assertEqual([], attribute_checker.normalize_and_verify(struct_ir))
127 size_attr = ir_util.get_attribute(struct_ir.module[0].type[0].attribute,
128 _FIXED_SIZE)
129 self.assertTrue(size_attr.expression)
130 self.assertEqual(64, ir_util.constant_value(size_attr.expression))
131
132 def test_rejects_incorrect_fixed_size_attribute_on_variable_size_struct(self):
133 struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
134 "struct Foo:\n"
135 " [fixed_size_in_bits: 8]\n"
136 " 0 [+4] UInt n\n"
137 " 4 [+n] UInt:8[] payload\n")
138 struct_type_ir = struct_ir.module[0].type[0]
139 self.assertEqual([[error.error(
140 "m.emb", struct_type_ir.attribute[0].value.source_location,
141 "Struct is marked as fixed size, but contains variable-location "
142 "fields.")]], attribute_checker.normalize_and_verify(struct_ir))
143
144 def test_rejects_size_attribute_with_wrong_large_value_on_struct(self):
145 struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
146 "struct Foo:\n"
147 " [fixed_size_in_bits: 80]\n"
148 " 0 [+2] UInt field1\n"
149 " 2 [+2] UInt field2\n"
150 " 4 [+4] UInt field3\n")
151 struct_type_ir = struct_ir.module[0].type[0]
152 self.assertEqual([
153 [error.error("m.emb", struct_type_ir.attribute[0].value.source_location,
154 "Struct is 64 bits, but is marked as 80 bits.")]
155 ], attribute_checker.normalize_and_verify(struct_ir))
156
157 def test_rejects_size_attribute_with_wrong_small_value_on_struct(self):
158 struct_ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
159 "struct Foo:\n"
160 " [fixed_size_in_bits: 40]\n"
161 " 0 [+2] UInt field1\n"
162 " 2 [+2] UInt field2\n"
163 " 4 [+4] UInt field3\n")
164 struct_type_ir = struct_ir.module[0].type[0]
165 self.assertEqual([
166 [error.error("m.emb", struct_type_ir.attribute[0].value.source_location,
167 "Struct is 64 bits, but is marked as 40 bits.")]
168 ], attribute_checker.normalize_and_verify(struct_ir))
169
170 def test_accepts_variable_size_external(self):
171 external_ir = _make_ir_from_emb("external Foo:\n"
172 " [addressable_unit_size: 1]\n")
173 self.assertEqual([], attribute_checker.normalize_and_verify(external_ir))
174
175 def test_accepts_fixed_size_external(self):
176 external_ir = _make_ir_from_emb("external Foo:\n"
177 " [fixed_size_in_bits: 32]\n"
178 " [addressable_unit_size: 1]\n")
179 self.assertEqual([], attribute_checker.normalize_and_verify(external_ir))
180
181 def test_rejects_external_with_no_addressable_unit_size_attribute(self):
182 external_ir = _make_ir_from_emb("external Foo:\n"
183 " [is_integer: false]\n")
184 external_type_ir = external_ir.module[0].type[0]
185 self.assertEqual([
186 [error.error(
187 "m.emb", external_type_ir.source_location,
188 "Expected 'addressable_unit_size' attribute for external type.")]
189 ], attribute_checker.normalize_and_verify(external_ir))
190
191 def test_rejects_is_integer_with_non_constant_value(self):
192 external_ir = _make_ir_from_emb(
193 "external Foo:\n"
194 " [is_integer: $static_size_in_bits == 1]\n"
195 " [addressable_unit_size: 1]\n")
196 external_type_ir = external_ir.module[0].type[0]
197 self.assertEqual([
198 [error.error(
199 "m.emb", external_type_ir.attribute[0].value.source_location,
200 "Attribute 'is_integer' must have a constant boolean value.")]
201 ], attribute_checker.normalize_and_verify(external_ir))
202
203 def test_rejects_addressable_unit_size_with_non_constant_value(self):
204 external_ir = _make_ir_from_emb(
205 "external Foo:\n"
206 " [is_integer: true]\n"
207 " [addressable_unit_size: $static_size_in_bits]\n")
208 external_type_ir = external_ir.module[0].type[0]
209 self.assertEqual([
210 [error.error(
211 "m.emb", external_type_ir.attribute[1].value.source_location,
212 "Attribute 'addressable_unit_size' must have a constant value.")]
213 ], attribute_checker.normalize_and_verify(external_ir))
214
215 def test_rejects_external_with_wrong_addressable_unit_size_attribute(self):
216 external_ir = _make_ir_from_emb("external Foo:\n"
217 " [addressable_unit_size: 4]\n")
218 external_type_ir = external_ir.module[0].type[0]
219 self.assertEqual([
220 [error.error(
221 "m.emb", external_type_ir.source_location,
222 "Only values '1' (bit) and '8' (byte) are allowed for the "
223 "'addressable_unit_size' attribute")]
224 ], attribute_checker.normalize_and_verify(external_ir))
225
226 def test_rejects_duplicate_attribute(self):
227 ir = _make_ir_from_emb("external Foo:\n"
228 " [is_integer: true]\n"
229 " [is_integer: true]\n")
230 self.assertEqual([[
231 error.error("m.emb", ir.module[0].type[0].attribute[1].source_location,
232 "Duplicate attribute 'is_integer'."),
233 error.note("m.emb", ir.module[0].type[0].attribute[0].source_location,
234 "Original attribute"),
235 ]], attribute_checker.normalize_and_verify(ir))
236
237 def test_rejects_duplicate_default_attribute(self):
238 ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
239 '[$default byte_order: "LittleEndian"]\n')
240 self.assertEqual(
241 [[
242 error.error("m.emb", ir.module[0].attribute[1].source_location,
243 "Duplicate attribute 'byte_order'."),
244 error.note("m.emb", ir.module[0].attribute[0].source_location,
245 "Original attribute"),
246 ]], attribute_checker.normalize_and_verify(ir))
247
248 def test_rejects_unknown_attribute(self):
249 ir = _make_ir_from_emb("[gibberish: true]\n")
250 attr = ir.module[0].attribute[0]
251 self.assertEqual([[
252 error.error("m.emb", attr.name.source_location,
253 "Unknown attribute 'gibberish' on module 'm.emb'.")
254 ]], attribute_checker.normalize_and_verify(ir))
255
256 def test_rejects_non_constant_attribute(self):
257 ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
258 "struct Foo:\n"
259 " [fixed_size_in_bits: field1]\n"
260 " 0 [+2] UInt field1\n")
261 attr = ir.module[0].type[0].attribute[0]
262 self.assertEqual(
263 [[
264 error.error(
265 "m.emb", attr.value.source_location,
266 "Attribute 'fixed_size_in_bits' must have a constant value.")
267 ]],
268 attribute_checker.normalize_and_verify(ir))
269
Ben Olmsteadc0d77842019-07-31 17:34:05 -0700270 def test_rejects_attribute_missing_required_back_end_specifier(self):
271 ir = _make_ir_from_emb('[namespace: "abc"]\n')
272 attr = ir.module[0].attribute[0]
273 self.assertEqual([[
274 error.error("m.emb", attr.name.source_location,
275 "Unknown attribute 'namespace' on module 'm.emb'.")
276 ]], attribute_checker.normalize_and_verify(ir))
277
Dmitri Prime5b381262023-05-01 23:55:07 -0700278 def test_accepts_attribute_with_default_known_back_end_specifier(self):
279 ir = _make_ir_from_emb('[(cpp) namespace: "abc"]\n')
280 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
281
282 def test_rejects_attribute_with_specified_back_end_specifier(self):
283 ir = _make_ir_from_emb('[(c) namespace: "abc"]\n'
284 '[expected_back_ends: "c, cpp"]\n')
285 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
286
287 def test_rejects_cpp_backend_attribute_when_not_in_expected_back_ends(self):
288 ir = _make_ir_from_emb('[(cpp) namespace: "abc"]\n'
289 '[expected_back_ends: "c"]\n')
Ben Olmsteadc0d77842019-07-31 17:34:05 -0700290 attr = ir.module[0].attribute[0]
Dmitri Prime5b381262023-05-01 23:55:07 -0700291 self.maxDiff = 200000
Ben Olmsteadc0d77842019-07-31 17:34:05 -0700292 self.assertEqual([[
Dmitri Prime5b381262023-05-01 23:55:07 -0700293 error.error(
294 "m.emb", attr.back_end.source_location,
295 "Back end specifier 'cpp' does not match any expected back end "
296 "specifier for this file: 'c'. Add or update the "
297 "'[expected_back_ends: \"c, cpp\"]' attribute at the file level if "
298 "this back end specifier is intentional.")
Ben Olmsteadc0d77842019-07-31 17:34:05 -0700299 ]], attribute_checker.normalize_and_verify(ir))
300
Dmitri Prime5b381262023-05-01 23:55:07 -0700301 def test_rejects_expected_back_ends_with_bad_back_end(self):
302 ir = _make_ir_from_emb('[expected_back_ends: "c++"]\n')
Ben Olmsteadc0d77842019-07-31 17:34:05 -0700303 attr = ir.module[0].attribute[0]
304 self.assertEqual([[
Dmitri Prime5b381262023-05-01 23:55:07 -0700305 error.error(
306 "m.emb", attr.value.source_location,
307 "Attribute 'expected_back_ends' must be a comma-delimited list of "
308 "back end specifiers (like \"cpp, proto\")), not \"c++\".")
Ben Olmsteadc0d77842019-07-31 17:34:05 -0700309 ]], attribute_checker.normalize_and_verify(ir))
310
Dmitri Prime5b381262023-05-01 23:55:07 -0700311 def test_rejects_expected_back_ends_with_no_comma(self):
312 ir = _make_ir_from_emb('[expected_back_ends: "cpp z"]\n')
313 attr = ir.module[0].attribute[0]
314 self.assertEqual([[
315 error.error(
316 "m.emb", attr.value.source_location,
317 "Attribute 'expected_back_ends' must be a comma-delimited list of "
318 "back end specifiers (like \"cpp, proto\")), not \"cpp z\".")
319 ]], attribute_checker.normalize_and_verify(ir))
320
321 def test_rejects_expected_back_ends_with_extra_commas(self):
322 ir = _make_ir_from_emb('[expected_back_ends: "cpp,,z"]\n')
323 attr = ir.module[0].attribute[0]
324 self.assertEqual([[
325 error.error(
326 "m.emb", attr.value.source_location,
327 "Attribute 'expected_back_ends' must be a comma-delimited list of "
328 "back end specifiers (like \"cpp, proto\")), not \"cpp,,z\".")
329 ]], attribute_checker.normalize_and_verify(ir))
330
331 def test_accepts_empty_expected_back_ends(self):
332 ir = _make_ir_from_emb('[expected_back_ends: ""]\n')
333 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
334
Ben Olmsteadc0d77842019-07-31 17:34:05 -0700335 def test_adds_byte_order_attributes_from_default(self):
336 ir = _make_ir_from_emb('[$default byte_order: "BigEndian"]\n'
337 "struct Foo:\n"
338 " 0 [+2] UInt bar\n"
339 " 2 [+2] UInt baz\n"
340 ' [byte_order: "LittleEndian"]\n')
341 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
342 byte_order_attr = ir_util.get_attribute(
343 ir.module[0].type[0].structure.field[0].attribute, _BYTE_ORDER)
344 self.assertTrue(byte_order_attr.HasField("string_constant"))
345 self.assertEqual("BigEndian", byte_order_attr.string_constant.text)
346 byte_order_attr = ir_util.get_attribute(
347 ir.module[0].type[0].structure.field[1].attribute, _BYTE_ORDER)
348 self.assertTrue(byte_order_attr.HasField("string_constant"))
349 self.assertEqual("LittleEndian", byte_order_attr.string_constant.text)
350
351 def test_adds_null_byte_order_attributes(self):
352 ir = _make_ir_from_emb("struct Foo:\n"
353 " 0 [+1] UInt bar\n"
354 " 1 [+1] UInt baz\n"
355 ' [byte_order: "LittleEndian"]\n'
356 " 2 [+2] UInt:8[] baseball\n"
357 " 4 [+2] UInt:8[] bat\n"
358 ' [byte_order: "LittleEndian"]\n')
359 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
360 structure = ir.module[0].type[0].structure
361 byte_order_attr = ir_util.get_attribute(
362 structure.field[0].attribute, _BYTE_ORDER)
363 self.assertTrue(byte_order_attr.HasField("string_constant"))
364 self.assertEqual("Null", byte_order_attr.string_constant.text)
365 self.assertEqual(structure.field[0].source_location,
366 byte_order_attr.source_location)
367 byte_order_attr = ir_util.get_attribute(structure.field[1].attribute,
368 _BYTE_ORDER)
369 self.assertTrue(byte_order_attr.HasField("string_constant"))
370 self.assertEqual("LittleEndian", byte_order_attr.string_constant.text)
371 byte_order_attr = ir_util.get_attribute(structure.field[2].attribute,
372 _BYTE_ORDER)
373 self.assertTrue(byte_order_attr.HasField("string_constant"))
374 self.assertEqual("Null", byte_order_attr.string_constant.text)
375 self.assertEqual(structure.field[2].source_location,
376 byte_order_attr.source_location)
377 byte_order_attr = ir_util.get_attribute(structure.field[3].attribute,
378 _BYTE_ORDER)
379 self.assertTrue(byte_order_attr.HasField("string_constant"))
380 self.assertEqual("LittleEndian", byte_order_attr.string_constant.text)
381
382 def test_disallows_default_byte_order_on_field(self):
383 ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
384 "struct Foo:\n"
385 " 0 [+2] UInt bar\n"
386 ' [$default byte_order: "LittleEndian"]\n')
387 default_byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
388 self.assertEqual(
389 [[error.error(
390 "m.emb", default_byte_order.name.source_location,
391 "Attribute 'byte_order' may not be defaulted on struct field 'bar'."
392 )]],
393 attribute_checker.normalize_and_verify(ir))
394
395 def test_disallows_default_byte_order_on_bits(self):
396 ir = _make_ir_from_emb("bits Foo:\n"
397 ' [$default byte_order: "LittleEndian"]\n'
398 " 0 [+2] UInt bar\n")
399 default_byte_order = ir.module[0].type[0].attribute[0]
400 self.assertEqual(
401 [[error.error(
402 "m.emb", default_byte_order.name.source_location,
403 "Attribute 'byte_order' may not be defaulted on bits 'Foo'.")]],
404 attribute_checker.normalize_and_verify(ir))
405
406 def test_disallows_default_byte_order_on_enum(self):
407 ir = _make_ir_from_emb("enum Foo:\n"
408 ' [$default byte_order: "LittleEndian"]\n'
409 " BAR = 1\n")
410 default_byte_order = ir.module[0].type[0].attribute[0]
411 self.assertEqual(
412 [[error.error(
413 "m.emb", default_byte_order.name.source_location,
414 "Attribute 'byte_order' may not be defaulted on enum 'Foo'.")]],
415 attribute_checker.normalize_and_verify(ir))
416
417 def test_adds_byte_order_from_scoped_default(self):
418 ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
419 "struct Foo:\n"
420 ' [$default byte_order: "BigEndian"]\n'
421 " 0 [+2] UInt bar\n")
422 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
423 byte_order_attr = ir_util.get_attribute(
424 ir.module[0].type[0].structure.field[0].attribute, _BYTE_ORDER)
425 self.assertTrue(byte_order_attr.HasField("string_constant"))
426 self.assertEqual("BigEndian", byte_order_attr.string_constant.text)
427
428 def test_disallows_unknown_byte_order(self):
429 ir = _make_ir_from_emb("struct Foo:\n"
430 " 0 [+2] UInt bar\n"
431 ' [byte_order: "NoEndian"]\n')
432 byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
433 self.assertEqual(
434 [[error.error(
435 "m.emb", byte_order.value.source_location,
436 "Attribute 'byte_order' must be 'BigEndian' or 'LittleEndian' or "
437 "'Null'.")]],
438 attribute_checker.normalize_and_verify(ir))
439
440 def test_disallows_unknown_default_byte_order(self):
441 ir = _make_ir_from_emb('[$default byte_order: "NoEndian"]\n')
442 default_byte_order = ir.module[0].attribute[0]
443 self.assertEqual(
444 [[error.error(
445 "m.emb", default_byte_order.value.source_location,
446 "Attribute 'byte_order' must be 'BigEndian' or 'LittleEndian' or "
447 "'Null'.")]],
448 attribute_checker.normalize_and_verify(ir))
449
450 def test_disallows_byte_order_on_non_byte_order_dependent_fields(self):
451 ir = _make_ir_from_emb("struct Foo:\n"
452 ' [$default byte_order: "LittleEndian"]\n'
453 " 0 [+2] UInt uint\n"
454 "struct Bar:\n"
455 " 0 [+2] Foo foo\n"
456 ' [byte_order: "LittleEndian"]\n')
457 byte_order = ir.module[0].type[1].structure.field[0].attribute[0]
458 self.assertEqual(
459 [[error.error(
460 "m.emb", byte_order.value.source_location,
461 "Attribute 'byte_order' not allowed on field which is not byte "
462 "order dependent.")]],
463 attribute_checker.normalize_and_verify(ir))
464
465 def test_disallows_byte_order_on_virtual_field(self):
466 ir = _make_ir_from_emb("struct Foo:\n"
467 " let x = 10\n"
468 ' [byte_order: "LittleEndian"]\n')
469 byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
470 self.assertEqual(
471 [[error.error(
472 "m.emb", byte_order.name.source_location,
473 "Unknown attribute 'byte_order' on virtual struct field 'x'.")]],
474 attribute_checker.normalize_and_verify(ir))
475
476 def test_disallows_null_byte_order_on_multibyte_fields(self):
477 ir = _make_ir_from_emb("struct Foo:\n"
478 " 0 [+2] UInt uint\n"
479 ' [byte_order: "Null"]\n')
480 byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
481 self.assertEqual(
482 [[error.error(
483 "m.emb", byte_order.value.source_location,
484 "Attribute 'byte_order' may only be 'Null' for one-byte fields.")]],
485 attribute_checker.normalize_and_verify(ir))
486
487 def test_disallows_null_byte_order_on_multibyte_array_elements(self):
488 ir = _make_ir_from_emb("struct Foo:\n"
489 " 0 [+4] UInt:16[] uint\n"
490 ' [byte_order: "Null"]\n')
491 byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
492 self.assertEqual(
493 [[error.error(
494 "m.emb", byte_order.value.source_location,
495 "Attribute 'byte_order' may only be 'Null' for one-byte fields.")]],
496 attribute_checker.normalize_and_verify(ir))
497
498 def test_requires_byte_order_on_byte_order_dependent_fields(self):
499 ir = _make_ir_from_emb("struct Foo:\n"
500 " 0 [+2] UInt uint\n")
501 field = ir.module[0].type[0].structure.field[0]
502 self.assertEqual(
503 [[error.error(
504 "m.emb", field.source_location,
505 "Attribute 'byte_order' required on field which is byte order "
506 "dependent.")]],
507 attribute_checker.normalize_and_verify(ir))
508
509 def test_disallows_unknown_text_output_attribute(self):
510 ir = _make_ir_from_emb("struct Foo:\n"
511 " 0 [+2] UInt bar\n"
512 ' [text_output: "None"]\n')
513 byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
514 self.assertEqual(
515 [[error.error(
516 "m.emb", byte_order.value.source_location,
517 "Attribute 'text_output' must be 'Emit' or 'Skip'.")]],
518 attribute_checker.normalize_and_verify(ir))
519
520 def test_disallows_non_string_text_output_attribute(self):
521 ir = _make_ir_from_emb("struct Foo:\n"
522 " 0 [+2] UInt bar\n"
523 " [text_output: 0]\n")
524 byte_order = ir.module[0].type[0].structure.field[0].attribute[0]
525 self.assertEqual(
526 [[error.error(
527 "m.emb", byte_order.value.source_location,
528 "Attribute 'text_output' must be 'Emit' or 'Skip'.")]],
529 attribute_checker.normalize_and_verify(ir))
530
531 def test_allows_skip_text_output_attribute_on_physical_field(self):
532 ir = _make_ir_from_emb("struct Foo:\n"
533 " 0 [+1] UInt bar\n"
534 ' [text_output: "Skip"]\n')
535 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
536
537 def test_allows_skip_text_output_attribute_on_virtual_field(self):
538 ir = _make_ir_from_emb("struct Foo:\n"
539 " let x = 10\n"
540 ' [text_output: "Skip"]\n')
541 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
542
543 def test_allows_emit_text_output_attribute_on_physical_field(self):
544 ir = _make_ir_from_emb("struct Foo:\n"
545 " 0 [+1] UInt bar\n"
546 ' [text_output: "Emit"]\n')
547 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
548
549 def test_adds_bit_addressable_unit_to_external(self):
550 external_ir = _make_ir_from_emb("external Foo:\n"
551 " [addressable_unit_size: 1]\n")
552 self.assertEqual([], attribute_checker.normalize_and_verify(external_ir))
Eric Rahm12c5e842024-03-22 09:37:24 -0700553 self.assertEqual(ir_data.AddressableUnit.BIT,
Ben Olmsteadc0d77842019-07-31 17:34:05 -0700554 external_ir.module[0].type[0].addressable_unit)
555
556 def test_adds_byte_addressable_unit_to_external(self):
557 external_ir = _make_ir_from_emb("external Foo:\n"
558 " [addressable_unit_size: 8]\n")
559 self.assertEqual([], attribute_checker.normalize_and_verify(external_ir))
Eric Rahm12c5e842024-03-22 09:37:24 -0700560 self.assertEqual(ir_data.AddressableUnit.BYTE,
Ben Olmsteadc0d77842019-07-31 17:34:05 -0700561 external_ir.module[0].type[0].addressable_unit)
562
563 def test_rejects_requires_using_array(self):
564 ir = _make_ir_from_emb("struct Foo:\n"
565 " 0 [+4] UInt:8[] array\n"
566 " [requires: this]\n")
567 field_ir = ir.module[0].type[0].structure.field[0]
568 self.assertEqual(
569 [[error.error("m.emb", field_ir.attribute[0].value.source_location,
570 "Attribute 'requires' must have a boolean value.")]],
571 attribute_checker.normalize_and_verify(ir))
572
573 def test_rejects_requires_on_array(self):
574 ir = _make_ir_from_emb("struct Foo:\n"
575 " 0 [+4] UInt:8[] array\n"
576 " [requires: false]\n")
577 field_ir = ir.module[0].type[0].structure.field[0]
578 self.assertEqual(
579 [[
580 error.error("m.emb", field_ir.attribute[0].value.source_location,
581 "Attribute 'requires' is only allowed on integer, "
582 "enumeration, or boolean fields, not arrays."),
583 error.note("m.emb", field_ir.type.source_location,
584 "Field type."),
585 ]],
586 error.filter_errors(attribute_checker.normalize_and_verify(ir)))
587
588 def test_rejects_requires_on_struct(self):
589 ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
590 "struct Foo:\n"
591 " 0 [+4] Bar bar\n"
592 " [requires: false]\n"
593 "struct Bar:\n"
594 " 0 [+4] UInt uint\n")
595 field_ir = ir.module[0].type[0].structure.field[0]
596 self.assertEqual(
597 [[error.error("m.emb", field_ir.attribute[0].value.source_location,
598 "Attribute 'requires' is only allowed on integer, "
599 "enumeration, or boolean fields.")]],
600 error.filter_errors(attribute_checker.normalize_and_verify(ir)))
601
602 def test_rejects_requires_on_float(self):
603 ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
604 "struct Foo:\n"
605 " 0 [+4] Float float\n"
606 " [requires: false]\n")
607 field_ir = ir.module[0].type[0].structure.field[0]
608 self.assertEqual(
609 [[error.error("m.emb", field_ir.attribute[0].value.source_location,
610 "Attribute 'requires' is only allowed on integer, "
611 "enumeration, or boolean fields.")]],
612 error.filter_errors(attribute_checker.normalize_and_verify(ir)))
613
reventlovc50913d2022-04-18 15:28:36 -0700614 def test_adds_false_is_signed_attribute(self):
615 ir = _make_ir_from_emb("enum Foo:\n"
616 " ZERO = 0\n")
617 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
618 enum = ir.module[0].type[0]
619 is_signed_attr = ir_util.get_attribute(enum.attribute, _IS_SIGNED)
620 self.assertTrue(is_signed_attr.expression.HasField("boolean_constant"))
621 self.assertFalse(is_signed_attr.expression.boolean_constant.value)
622
623 def test_leaves_is_signed_attribute(self):
624 ir = _make_ir_from_emb("enum Foo:\n"
625 " [is_signed: true]\n"
626 " ZERO = 0\n")
627 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
628 enum = ir.module[0].type[0]
629 is_signed_attr = ir_util.get_attribute(enum.attribute, _IS_SIGNED)
630 self.assertTrue(is_signed_attr.expression.HasField("boolean_constant"))
631 self.assertTrue(is_signed_attr.expression.boolean_constant.value)
632
633 def test_adds_true_is_signed_attribute(self):
634 ir = _make_ir_from_emb("enum Foo:\n"
635 " NEGATIVE_ONE = -1\n")
636 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
637 enum = ir.module[0].type[0]
638 is_signed_attr = ir_util.get_attribute(enum.attribute, _IS_SIGNED)
639 self.assertTrue(is_signed_attr.expression.HasField("boolean_constant"))
640 self.assertTrue(is_signed_attr.expression.boolean_constant.value)
641
642 def test_adds_max_bits_attribute(self):
643 ir = _make_ir_from_emb("enum Foo:\n"
644 " ZERO = 0\n")
645 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
646 enum = ir.module[0].type[0]
647 max_bits_attr = ir_util.get_attribute(enum.attribute, _MAX_BITS)
648 self.assertTrue(max_bits_attr.expression.HasField("constant"))
649 self.assertEqual("64", max_bits_attr.expression.constant.value)
650
651 def test_leaves_max_bits_attribute(self):
652 ir = _make_ir_from_emb("enum Foo:\n"
653 " [maximum_bits: 32]\n"
654 " ZERO = 0\n")
655 self.assertEqual([], attribute_checker.normalize_and_verify(ir))
656 enum = ir.module[0].type[0]
657 max_bits_attr = ir_util.get_attribute(enum.attribute, _MAX_BITS)
658 self.assertTrue(max_bits_attr.expression.HasField("constant"))
659 self.assertEqual("32", max_bits_attr.expression.constant.value)
660
661 def test_rejects_too_small_max_bits(self):
662 ir = _make_ir_from_emb("enum Foo:\n"
663 " [maximum_bits: 0]\n"
664 " ZERO = 0\n")
665 attribute_ir = ir.module[0].type[0].attribute[0]
666 self.assertEqual(
667 [[error.error(
668 "m.emb", attribute_ir.value.source_location,
669 "'maximum_bits' on an 'enum' must be between 1 and 64.")]],
670 error.filter_errors(attribute_checker.normalize_and_verify(ir)))
671
672 def test_rejects_too_large_max_bits(self):
673 ir = _make_ir_from_emb("enum Foo:\n"
674 " [maximum_bits: 65]\n"
675 " ZERO = 0\n")
676 attribute_ir = ir.module[0].type[0].attribute[0]
677 self.assertEqual(
678 [[error.error(
679 "m.emb", attribute_ir.value.source_location,
680 "'maximum_bits' on an 'enum' must be between 1 and 64.")]],
681 error.filter_errors(attribute_checker.normalize_and_verify(ir)))
682
Jason Graffius11197232023-05-17 19:12:14 -0400683 def test_rejects_unknown_enum_value_attribute(self):
684 ir = _make_ir_from_emb("enum Foo:\n"
685 " BAR = 0 \n"
686 " [bad_attr: true]\n")
687 attribute_ir = ir.module[0].type[0].enumeration.value[0].attribute[0]
688 self.assertNotEqual([], attribute_checker.normalize_and_verify(ir))
689 self.assertEqual(
690 [[error.error(
691 "m.emb", attribute_ir.name.source_location,
692 "Unknown attribute 'bad_attr' on enum value 'BAR'.")]],
693 error.filter_errors(attribute_checker.normalize_and_verify(ir)))
694
Ben Olmsteadc0d77842019-07-31 17:34:05 -0700695
696if __name__ == "__main__":
697 unittest.main()