xref: /aosp_15_r20/external/emboss/compiler/front_end/constraints_test.py (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
1# 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 constraints.py."""
16
17import unittest
18from compiler.front_end import attributes
19from compiler.front_end import constraints
20from compiler.front_end import glue
21from compiler.util import error
22from compiler.util import ir_data_utils
23from compiler.util import ir_util
24from compiler.util import test_util
25
26
27def _make_ir_from_emb(emb_text, name="m.emb"):
28  ir, unused_debug_info, errors = glue.parse_emboss_file(
29      name,
30      test_util.dict_file_reader({name: emb_text}),
31      stop_before_step="check_constraints")
32  assert not errors, repr(errors)
33  return ir
34
35
36class ConstraintsTest(unittest.TestCase):
37  """Tests constraints.check_constraints and helpers."""
38
39  def test_error_on_missing_inner_array_size(self):
40    ir = _make_ir_from_emb("struct Foo:\n"
41                           "  0 [+1]  UInt:8[][1]  one_byte\n")
42    # There is a latent issue here where the source location reported in this
43    # error is using a default value of 0:0. An issue is filed at
44    # https://github.com/google/emboss/issues/153 for further investigation.
45    # In the meantime we use `ir_data_utils.reader` to mimic this legacy
46    # behavior.
47    error_array = ir_data_utils.reader(
48        ir.module[0].type[0].structure.field[0].type.array_type)
49    self.assertEqual([[
50        error.error(
51            "m.emb",
52            error_array.base_type.array_type.element_count.source_location,
53            "Array dimensions can only be omitted for the outermost dimension.")
54    ]], error.filter_errors(constraints.check_constraints(ir)))
55
56  def test_no_error_on_ok_array_size(self):
57    ir = _make_ir_from_emb("struct Foo:\n"
58                           "  0 [+1]  UInt:8[1][1]  one_byte\n")
59    self.assertEqual([], constraints.check_constraints(ir))
60
61  def test_no_error_on_ok_missing_outer_array_size(self):
62    ir = _make_ir_from_emb("struct Foo:\n"
63                           "  0 [+1]  UInt:8[1][]  one_byte\n")
64    self.assertEqual([], constraints.check_constraints(ir))
65
66  def test_no_error_on_dynamically_sized_struct_in_dynamically_sized_field(
67      self):
68    ir = _make_ir_from_emb("struct Foo:\n"
69                           "  0 [+1]     UInt  size\n"
70                           "  1 [+size]  Bar   bar\n"
71                           "struct Bar:\n"
72                           "  0 [+1]     UInt      size\n"
73                           "  1 [+size]  UInt:8[]  payload\n")
74    self.assertEqual([], constraints.check_constraints(ir))
75
76  def test_no_error_on_dynamically_sized_struct_in_statically_sized_field(self):
77    ir = _make_ir_from_emb("struct Foo:\n"
78                           "  0 [+10]  Bar   bar\n"
79                           "struct Bar:\n"
80                           "  0 [+1]     UInt      size\n"
81                           "  1 [+size]  UInt:8[]  payload\n")
82    self.assertEqual([], constraints.check_constraints(ir))
83
84  def test_no_error_non_fixed_size_outer_array_dimension(self):
85    ir = _make_ir_from_emb("struct Foo:\n"
86                           "  0 [+1]     UInt               size\n"
87                           "  1 [+size]  UInt:8[1][size-1]  one_byte\n")
88    self.assertEqual([], constraints.check_constraints(ir))
89
90  def test_error_non_fixed_size_inner_array_dimension(self):
91    ir = _make_ir_from_emb("struct Foo:\n"
92                           "  0 [+1]     UInt               size\n"
93                           "  1 [+size]  UInt:8[size-1][1]  one_byte\n")
94    error_array = ir.module[0].type[0].structure.field[1].type.array_type
95    self.assertEqual([[
96        error.error(
97            "m.emb",
98            error_array.base_type.array_type.element_count.source_location,
99            "Inner array dimensions must be constant.")
100    ]], error.filter_errors(constraints.check_constraints(ir)))
101
102  def test_error_non_constant_inner_array_dimensions(self):
103    ir = _make_ir_from_emb("struct Foo:\n"
104                           "  0 [+1]  Bar[1]  one_byte\n"
105                           # There is no dynamically-sized byte-oriented type in
106                           # the Prelude, so this test has to make its own.
107                           "external Bar:\n"
108                           "  [is_integer: true]\n"
109                           "  [addressable_unit_size: 8]\n")
110    error_array = ir.module[0].type[0].structure.field[0].type.array_type
111    self.assertEqual([[
112        error.error(
113            "m.emb", error_array.base_type.atomic_type.source_location,
114            "Array elements must be fixed size.")
115    ]], error.filter_errors(constraints.check_constraints(ir)))
116
117  def test_error_dynamically_sized_array_elements(self):
118    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
119                           "struct Foo:\n"
120                           "  0 [+1]  Bar[1]  bar\n"
121                           "struct Bar:\n"
122                           "  0 [+1]     UInt      size\n"
123                           "  1 [+size]  UInt:8[]  payload\n")
124    error_array = ir.module[0].type[0].structure.field[0].type.array_type
125    self.assertEqual([[
126        error.error(
127            "m.emb", error_array.base_type.atomic_type.source_location,
128            "Array elements must be fixed size.")
129    ]], error.filter_errors(constraints.check_constraints(ir)))
130
131  def test_field_too_small_for_type(self):
132    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
133                           "struct Foo:\n"
134                           "  0 [+1]  Bar  bar\n"
135                           "struct Bar:\n"
136                           "  0 [+2]  UInt  value\n")
137    error_type = ir.module[0].type[0].structure.field[0].type
138    self.assertEqual([[
139        error.error(
140            "m.emb", error_type.source_location,
141            "Fixed-size type 'Bar' cannot be placed in field of size 8 bits; "
142            "requires 16 bits.")
143    ]], error.filter_errors(constraints.check_constraints(ir)))
144
145  def test_dynamically_sized_field_always_too_small_for_type(self):
146    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
147                           "struct Foo:\n"
148                           "  0 [+1]  bits:\n"
149                           "    0 [+1]  UInt  x\n"
150                           "  0 [+x]  Bar  bar\n"
151                           "struct Bar:\n"
152                           "  0 [+2]  UInt  value\n")
153    error_type = ir.module[0].type[0].structure.field[2].type
154    self.assertEqual([[
155        error.error(
156            "m.emb", error_type.source_location,
157            "Field of maximum size 8 bits cannot hold fixed-size type 'Bar', "
158            "which requires 16 bits.")
159    ]], error.filter_errors(constraints.check_constraints(ir)))
160
161  def test_struct_field_too_big_for_type(self):
162    ir = _make_ir_from_emb("struct Foo:\n"
163                           "  0 [+2]  Byte  double_byte\n"
164                           "struct Byte:\n"
165                           "  0 [+1]  UInt  b\n")
166    error_type = ir.module[0].type[0].structure.field[0].type
167    self.assertEqual([[
168        error.error(
169            "m.emb", error_type.source_location,
170            "Fixed-size type 'Byte' cannot be placed in field of size 16 bits; "
171            "requires 8 bits.")
172    ]], error.filter_errors(constraints.check_constraints(ir)))
173
174  def test_bits_field_too_big_for_type(self):
175    ir = _make_ir_from_emb("struct Foo:\n"
176                           "  0 [+9]  UInt  uint72\n"
177                           '    [byte_order: "LittleEndian"]\n')
178    error_field = ir.module[0].type[0].structure.field[0]
179    uint_type = ir_util.find_object(error_field.type.atomic_type.reference, ir)
180    uint_requirements = ir_util.get_attribute(uint_type.attribute,
181                                              attributes.STATIC_REQUIREMENTS)
182    self.assertEqual([[
183        error.error("m.emb", error_field.source_location,
184                    "Requirements of UInt not met."),
185        error.note("", uint_requirements.source_location,
186                   "Requirements specified here."),
187    ]], error.filter_errors(constraints.check_constraints(ir)))
188
189  def test_field_type_not_allowed_in_bits(self):
190    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
191                           "bits Foo:\n"
192                           "  0 [+16]  Bar  bar\n"
193                           "external Bar:\n"
194                           "  [addressable_unit_size: 8]\n")
195    error_type = ir.module[0].type[0].structure.field[0].type
196    self.assertEqual([[
197        error.error(
198            "m.emb", error_type.source_location,
199            "Byte-oriented type 'Bar' cannot be used in a bits field.")
200    ]], error.filter_errors(constraints.check_constraints(ir)))
201
202  def test_arrays_allowed_in_bits(self):
203    ir = _make_ir_from_emb("bits Foo:\n"
204                           "  0 [+16]  Flag[16]  bar\n")
205    self.assertEqual([], constraints.check_constraints(ir))
206
207  def test_oversized_anonymous_bit_field(self):
208    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
209                           "struct Foo:\n"
210                           "  0 [+4]  bits:\n"
211                           "    0 [+8]  UInt  field\n")
212    self.assertEqual([], constraints.check_constraints(ir))
213
214  def test_undersized_anonymous_bit_field(self):
215    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
216                           "struct Foo:\n"
217                           "  0 [+1]  bits:\n"
218                           "    0 [+32]  UInt  field\n")
219    error_type = ir.module[0].type[0].structure.field[0].type
220    self.assertEqual([[
221        error.error(
222            "m.emb", error_type.source_location,
223            "Fixed-size anonymous type cannot be placed in field of size 8 "
224            "bits; requires 32 bits.")
225    ]], error.filter_errors(constraints.check_constraints(ir)))
226
227  def test_reserved_field_name(self):
228    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
229                           "struct Foo:\n"
230                           "  0 [+8]  UInt  restrict\n")
231    error_name = ir.module[0].type[0].structure.field[0].name.name
232    self.assertEqual([[
233        error.error(
234            "m.emb", error_name.source_location,
235            "C reserved word may not be used as a field name.")
236    ]], error.filter_errors(constraints.check_constraints(ir)))
237
238  def test_reserved_type_name(self):
239    ir = _make_ir_from_emb("struct False:\n"
240                           "  0 [+1]  UInt  foo\n")
241    error_name = ir.module[0].type[0].name.name
242    self.assertEqual([[
243        error.error(
244            "m.emb", error_name.source_location,
245            "Python 3 reserved word may not be used as a type name.")
246    ]], error.filter_errors(constraints.check_constraints(ir)))
247
248  def test_reserved_enum_name(self):
249    ir = _make_ir_from_emb("enum Foo:\n"
250                           "  NULL = 1\n")
251    error_name = ir.module[0].type[0].enumeration.value[0].name.name
252    self.assertEqual([[
253        error.error(
254            "m.emb", error_name.source_location,
255            "C reserved word may not be used as an enum name.")
256    ]], error.filter_errors(constraints.check_constraints(ir)))
257
258  def test_bits_type_in_struct_array(self):
259    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
260                           "struct Foo:\n"
261                           "  0 [+10]  UInt:8[10]  array\n")
262    self.assertEqual([], constraints.check_constraints(ir))
263
264  def test_bits_type_in_bits_array(self):
265    ir = _make_ir_from_emb("bits Foo:\n"
266                           "  0 [+10]  UInt:8[10]  array\n")
267    self.assertEqual([], constraints.check_constraints(ir))
268
269  def test_explicit_size_too_small(self):
270    ir = _make_ir_from_emb("bits Foo:\n"
271                           "  0 [+0]  UInt:0  zero_bit\n")
272    error_field = ir.module[0].type[0].structure.field[0]
273    uint_type = ir_util.find_object(error_field.type.atomic_type.reference, ir)
274    uint_requirements = ir_util.get_attribute(uint_type.attribute,
275                                              attributes.STATIC_REQUIREMENTS)
276    self.assertEqual([[
277        error.error("m.emb", error_field.source_location,
278                    "Requirements of UInt not met."),
279        error.note("", uint_requirements.source_location,
280                   "Requirements specified here."),
281    ]], error.filter_errors(constraints.check_constraints(ir)))
282
283  def test_explicit_enumeration_size_too_small(self):
284    ir = _make_ir_from_emb('[$default byte_order: "BigEndian"]\n'
285                           "bits Foo:\n"
286                           "  0 [+0]  Bar:0  zero_bit\n"
287                           "enum Bar:\n"
288                           "  BAZ = 0\n")
289    error_type = ir.module[0].type[0].structure.field[0].type
290    self.assertEqual([[
291        error.error("m.emb", error_type.source_location,
292                    "Enumeration type 'Bar' cannot be 0 bits; type 'Bar' "
293                    "must be between 1 and 64 bits, inclusive."),
294    ]], error.filter_errors(constraints.check_constraints(ir)))
295
296  def test_explicit_size_too_big_for_field(self):
297    ir = _make_ir_from_emb("bits Foo:\n"
298                           "  0 [+8]  UInt:32  thirty_two_bit\n")
299    error_type = ir.module[0].type[0].structure.field[0].type
300    self.assertEqual([[
301        error.error(
302            "m.emb", error_type.source_location,
303            "Fixed-size type 'UInt:32' cannot be placed in field of size 8 "
304            "bits; requires 32 bits.")
305    ]], error.filter_errors(constraints.check_constraints(ir)))
306
307  def test_explicit_size_too_small_for_field(self):
308    ir = _make_ir_from_emb("bits Foo:\n"
309                           "  0 [+64]  UInt:32  thirty_two_bit\n")
310    error_type = ir.module[0].type[0].structure.field[0].type
311    self.assertEqual([[
312        error.error("m.emb", error_type.source_location,
313                    "Fixed-size type 'UInt:32' cannot be placed in field of "
314                    "size 64 bits; requires 32 bits.")
315    ]], error.filter_errors(constraints.check_constraints(ir)))
316
317  def test_explicit_size_too_big(self):
318    ir = _make_ir_from_emb("struct Foo:\n"
319                           "  0 [+16]  UInt:128  one_twenty_eight_bit\n"
320                           '    [byte_order: "LittleEndian"]\n')
321    error_field = ir.module[0].type[0].structure.field[0]
322    uint_type = ir_util.find_object(error_field.type.atomic_type.reference, ir)
323    uint_requirements = ir_util.get_attribute(uint_type.attribute,
324                                              attributes.STATIC_REQUIREMENTS)
325    self.assertEqual([[
326        error.error("m.emb", error_field.source_location,
327                    "Requirements of UInt not met."),
328        error.note("", uint_requirements.source_location,
329                   "Requirements specified here."),
330    ]], error.filter_errors(constraints.check_constraints(ir)))
331
332  def test_explicit_enumeration_size_too_big(self):
333    ir = _make_ir_from_emb('[$default byte_order: "BigEndian"]\n'
334                           "struct Foo:\n"
335                           "  0 [+9]  Bar  seventy_two_bit\n"
336                           "enum Bar:\n"
337                           "  BAZ = 0\n")
338    error_type = ir.module[0].type[0].structure.field[0].type
339    self.assertEqual([[
340        error.error("m.emb", error_type.source_location,
341                    "Enumeration type 'Bar' cannot be 72 bits; type 'Bar' " +
342                    "must be between 1 and 64 bits, inclusive."),
343    ]], error.filter_errors(constraints.check_constraints(ir)))
344
345  def test_explicit_enumeration_size_too_big_for_small_enum(self):
346    ir = _make_ir_from_emb('[$default byte_order: "BigEndian"]\n'
347                           "struct Foo:\n"
348                           "  0 [+8]  Bar  sixty_four_bit\n"
349                           "enum Bar:\n"
350                           "  [maximum_bits: 63]\n"
351                           "  BAZ = 0\n")
352    error_type = ir.module[0].type[0].structure.field[0].type
353    self.assertEqual([[
354        error.error("m.emb", error_type.source_location,
355                    "Enumeration type 'Bar' cannot be 64 bits; type 'Bar' " +
356                    "must be between 1 and 63 bits, inclusive."),
357    ]], error.filter_errors(constraints.check_constraints(ir)))
358
359  def test_explicit_size_on_fixed_size_type(self):
360    ir = _make_ir_from_emb("struct Foo:\n"
361                           "  0 [+1]  Byte:8  one_byte\n"
362                           "struct Byte:\n"
363                           "  0 [+1]  UInt  b\n")
364    self.assertEqual([], constraints.check_constraints(ir))
365
366  def test_explicit_size_too_small_on_fixed_size_type(self):
367    ir = _make_ir_from_emb("struct Foo:\n"
368                           "  0 [+0]  Byte:0  null_byte\n"
369                           "struct Byte:\n"
370                           "  0 [+1]  UInt  b\n")
371    error_type = ir.module[0].type[0].structure.field[0].type
372    self.assertEqual([[
373        error.error(
374            "m.emb", error_type.size_in_bits.source_location,
375            "Explicit size of 0 bits does not match fixed size (8 bits) of "
376            "type 'Byte'."),
377        error.note("m.emb", ir.module[0].type[1].source_location,
378                   "Size specified here."),
379    ]], error.filter_errors(constraints.check_constraints(ir)))
380
381  def test_explicit_size_too_big_on_fixed_size_type(self):
382    ir = _make_ir_from_emb("struct Foo:\n"
383                           "  0 [+2]  Byte:16  double_byte\n"
384                           "struct Byte:\n"
385                           "  0 [+1]  UInt  b\n")
386    error_type = ir.module[0].type[0].structure.field[0].type
387    self.assertEqual([[
388        error.error(
389            "m.emb", error_type.size_in_bits.source_location,
390            "Explicit size of 16 bits does not match fixed size (8 bits) of "
391            "type 'Byte'."),
392        error.note(
393            "m.emb", ir.module[0].type[1].source_location,
394            "Size specified here."),
395    ]], error.filter_errors(constraints.check_constraints(ir)))
396
397  def test_explicit_size_ignored_on_variable_size_type(self):
398    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
399                           "struct Foo:\n"
400                           "  0 [+1]  UInt      n\n"
401                           "  1 [+n]  UInt:8[]  d\n"
402                           "struct Bar:\n"
403                           "  0 [+10]  Foo:80  foo\n")
404    self.assertEqual([], constraints.check_constraints(ir))
405
406  def test_fixed_size_type_in_dynamically_sized_field(self):
407    ir = _make_ir_from_emb("struct Foo:\n"
408                           "  0 [+1]    UInt  bar\n"
409                           "  0 [+bar]  Byte  one_byte\n"
410                           "struct Byte:\n"
411                           "  0 [+1]  UInt  b\n")
412    self.assertEqual([], constraints.check_constraints(ir))
413
414  def test_enum_in_dynamically_sized_field(self):
415    ir = _make_ir_from_emb('[$default byte_order: "BigEndian"]\n'
416                           "struct Foo:\n"
417                           "  0 [+1]    UInt  bar\n"
418                           "  0 [+bar]  Baz   baz\n"
419                           "enum Baz:\n"
420                           "  QUX = 0\n")
421    error_type = ir.module[0].type[0].structure.field[1].type
422    self.assertEqual(
423        [[
424            error.error("m.emb", error_type.source_location,
425                        "Enumeration type 'Baz' cannot be placed in a "
426                        "dynamically-sized field.")
427        ]],
428        error.filter_errors(constraints.check_constraints(ir)))
429
430  def test_enum_value_too_high(self):
431    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
432                           "enum Foo:\n"
433                           "  HIGH = 0x1_0000_0000_0000_0000\n")
434    error_value = ir.module[0].type[0].enumeration.value[0].value
435    self.assertEqual([
436        [error.error(
437            "m.emb", error_value.source_location,
438            # TODO(bolms): Try to print numbers like 2**64 in hex?  (I.e., if a
439            # number is a round number in hex, but not in decimal, print in
440            # hex?)
441            "Value 18446744073709551616 is out of range for 64-bit unsigned " +
442            "enumeration.")]
443    ], constraints.check_constraints(ir))
444
445  def test_enum_value_too_low(self):
446    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
447                           "enum Foo:\n"
448                           "  LOW = -0x8000_0000_0000_0001\n")
449    error_value = ir.module[0].type[0].enumeration.value[0].value
450    self.assertEqual([
451        [error.error(
452            "m.emb", error_value.source_location,
453            "Value -9223372036854775809 is out of range for 64-bit signed " +
454            "enumeration.")]
455    ], constraints.check_constraints(ir))
456
457  def test_enum_value_too_wide(self):
458    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
459                           "enum Foo:\n"
460                           "  LOW = -1\n"
461                           "  HIGH = 0x8000_0000_0000_0000\n")
462    error_value = ir.module[0].type[0].enumeration.value[1].value
463    self.assertEqual([[
464        error.error(
465            "m.emb", error_value.source_location,
466            "Value 9223372036854775808 is out of range for 64-bit signed " +
467            "enumeration.")
468    ]], error.filter_errors(constraints.check_constraints(ir)))
469
470  def test_enum_value_too_wide_unsigned_error_message(self):
471    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
472                           "enum Foo:\n"
473                           "  LOW = -2\n"
474                           "  LOW2 = -1\n"
475                           "  HIGH = 0x8000_0000_0000_0000\n")
476    error_value = ir.module[0].type[0].enumeration.value[2].value
477    self.assertEqual([[
478        error.error(
479            "m.emb", error_value.source_location,
480            "Value 9223372036854775808 is out of range for 64-bit signed " +
481            "enumeration.")
482    ]], error.filter_errors(constraints.check_constraints(ir)))
483
484  def test_enum_value_too_wide_small_size_error_message(self):
485    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
486                           "enum Foo:\n"
487                           "  [maximum_bits: 8]\n"
488                           "  HIGH = 0x100\n")
489    error_value = ir.module[0].type[0].enumeration.value[0].value
490    self.assertEqual([[
491        error.error(
492            "m.emb", error_value.source_location,
493            "Value 256 is out of range for 8-bit unsigned enumeration.")
494    ]], error.filter_errors(constraints.check_constraints(ir)))
495
496  def test_enum_value_too_wide_small_size_signed_error_message(self):
497    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
498                           "enum Foo:\n"
499                           "  [maximum_bits: 8]\n"
500                           "  [is_signed: true]\n"
501                           "  HIGH = 0x80\n")
502    error_value = ir.module[0].type[0].enumeration.value[0].value
503    self.assertEqual([[
504        error.error(
505            "m.emb", error_value.source_location,
506            "Value 128 is out of range for 8-bit signed enumeration.")
507    ]], error.filter_errors(constraints.check_constraints(ir)))
508
509  def test_enum_value_too_wide_multiple(self):
510    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
511                           "enum Foo:\n"
512                           "  LOW = -2\n"
513                           "  LOW2 = -1\n"
514                           "  HIGH = 0x8000_0000_0000_0000\n"
515                           "  HIGH2 = 0x8000_0000_0000_0001\n")
516    error_value = ir.module[0].type[0].enumeration.value[2].value
517    error_value2 = ir.module[0].type[0].enumeration.value[3].value
518    self.assertEqual([
519        [error.error(
520            "m.emb", error_value.source_location,
521            "Value 9223372036854775808 is out of range for 64-bit signed " +
522            "enumeration.")],
523        [error.error(
524            "m.emb", error_value2.source_location,
525            "Value 9223372036854775809 is out of range for 64-bit signed " +
526            "enumeration.")]
527    ], error.filter_errors(constraints.check_constraints(ir)))
528
529  def test_enum_value_too_wide_multiple_signed_error_message(self):
530    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
531                           "enum Foo:\n"
532                           "  LOW = -3\n"
533                           "  LOW2 = -2\n"
534                           "  LOW3 = -1\n"
535                           "  HIGH = 0x8000_0000_0000_0000\n"
536                           "  HIGH2 = 0x8000_0000_0000_0001\n")
537    error_value = ir.module[0].type[0].enumeration.value[3].value
538    error_value2 = ir.module[0].type[0].enumeration.value[4].value
539    self.assertEqual([
540        [error.error(
541            "m.emb", error_value.source_location,
542            "Value 9223372036854775808 is out of range for 64-bit signed "
543            "enumeration.")],
544        [error.error(
545            "m.emb", error_value2.source_location,
546            "Value 9223372036854775809 is out of range for 64-bit signed "
547            "enumeration.")]
548    ], error.filter_errors(constraints.check_constraints(ir)))
549
550  def test_enum_value_mixed_error_message(self):
551    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
552                           "enum Foo:\n"
553                           "  LOW = -1\n"
554                           "  HIGH = 0x8000_0000_0000_0000\n"
555                           "  HIGH2 = 0x1_0000_0000_0000_0000\n")
556    error_value1 = ir.module[0].type[0].enumeration.value[1].value
557    error_value2 = ir.module[0].type[0].enumeration.value[2].value
558    self.assertEqual([
559        [error.error(
560            "m.emb", error_value1.source_location,
561            "Value 9223372036854775808 is out of range for 64-bit signed " +
562            "enumeration.")],
563        [error.error(
564            "m.emb", error_value2.source_location,
565            "Value 18446744073709551616 is out of range for 64-bit signed " +
566            "enumeration.")]
567    ], error.filter_errors(constraints.check_constraints(ir)))
568
569  def test_enum_value_explicitly_signed_error_message(self):
570    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
571                           "enum Foo:\n"
572                           "  [is_signed: true]\n"
573                           "  HIGH = 0x8000_0000_0000_0000\n"
574                           "  HIGH2 = 0x1_0000_0000_0000_0000\n")
575    error_value0 = ir.module[0].type[0].enumeration.value[0].value
576    error_value1 = ir.module[0].type[0].enumeration.value[1].value
577    self.assertEqual([
578        [error.error(
579            "m.emb", error_value0.source_location,
580            "Value 9223372036854775808 is out of range for 64-bit signed " +
581            "enumeration.")],
582        [error.error(
583            "m.emb", error_value1.source_location,
584            "Value 18446744073709551616 is out of range for 64-bit signed " +
585            "enumeration.")]
586    ], error.filter_errors(constraints.check_constraints(ir)))
587
588  def test_enum_value_explicitly_unsigned_error_message(self):
589    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
590                           "enum Foo:\n"
591                           "  [is_signed: false]\n"
592                           "  LOW = -1\n"
593                           "  HIGH = 0x8000_0000_0000_0000\n"
594                           "  HIGH2 = 0x1_0000_0000_0000_0000\n")
595    error_value0 = ir.module[0].type[0].enumeration.value[0].value
596    error_value2 = ir.module[0].type[0].enumeration.value[2].value
597    self.assertEqual([
598        [error.error(
599            "m.emb", error_value0.source_location,
600            "Value -1 is out of range for 64-bit unsigned enumeration.")],
601        [error.error(
602            "m.emb", error_value2.source_location,
603            "Value 18446744073709551616 is out of range for 64-bit unsigned " +
604            "enumeration.")]
605    ], error.filter_errors(constraints.check_constraints(ir)))
606
607  def test_explicit_non_byte_size_array_element(self):
608    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
609                           "struct Foo:\n"
610                           "  0 [+2]  UInt:4[4]  nibbles\n")
611    error_type = ir.module[0].type[0].structure.field[0].type.array_type
612    self.assertEqual([
613        [error.error(
614            "m.emb", error_type.base_type.source_location,
615            "Array elements in structs must have sizes which are a multiple of "
616            "8 bits.")]
617    ], error.filter_errors(constraints.check_constraints(ir)))
618
619  def test_implicit_non_byte_size_array_element(self):
620    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
621                           "bits Nibble:\n"
622                           "  0 [+4]  UInt  nibble\n"
623                           "struct Foo:\n"
624                           "  0 [+2]  Nibble[4]  nibbles\n")
625    error_type = ir.module[0].type[1].structure.field[0].type.array_type
626    self.assertEqual([
627        [error.error(
628            "m.emb", error_type.base_type.source_location,
629            "Array elements in structs must have sizes which are a multiple of "
630            "8 bits.")]
631    ], error.filter_errors(constraints.check_constraints(ir)))
632
633  def test_bits_must_be_fixed_size(self):
634    ir = _make_ir_from_emb("bits Dynamic:\n"
635                           "  0 [+3]      UInt       x\n"
636                           "  3 [+3 * x]  UInt:3[x]  a\n")
637    error_type = ir.module[0].type[0]
638    self.assertEqual([
639        [error.error("m.emb", error_type.source_location,
640                     "`bits` types must be fixed size.")]
641    ], error.filter_errors(constraints.check_constraints(ir)))
642
643  def test_bits_must_be_small(self):
644    ir = _make_ir_from_emb("bits Big:\n"
645                           "  0  [+64]  UInt  x\n"
646                           "  64 [+1]   UInt  y\n")
647    error_type = ir.module[0].type[0]
648    self.assertEqual([
649        [error.error("m.emb", error_type.source_location,
650                     "`bits` types must be 64 bits or smaller.")]
651    ], error.filter_errors(constraints.check_constraints(ir)))
652
653  def test_constant_expressions_must_be_small(self):
654    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
655                           "struct Foo:\n"
656                           "  0  [+8]   UInt  x\n"
657                           "  if x < 0x1_0000_0000_0000_0000:\n"
658                           "    8 [+1]   UInt  y\n")
659    condition = ir.module[0].type[0].structure.field[1].existence_condition
660    error_location = condition.function.args[1].source_location
661    self.assertEqual([
662        [error.error(
663            "m.emb", error_location,
664            "Constant value {} of expression cannot fit in a 64-bit signed or "
665            "unsigned integer.".format(2**64))]
666    ], error.filter_errors(constraints.check_constraints(ir)))
667
668  def test_variable_expression_out_of_range_for_uint64(self):
669    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
670                           "struct Foo:\n"
671                           "  0  [+8]   UInt  x\n"
672                           "  if x + 1 < 0xffff_ffff_ffff_ffff:\n"
673                           "    8 [+1]   UInt  y\n")
674    condition = ir.module[0].type[0].structure.field[1].existence_condition
675    error_location = condition.function.args[0].source_location
676    self.assertEqual([
677        [error.error(
678            "m.emb", error_location,
679            "Potential range of expression is {} to {}, which cannot fit in a "
680            "64-bit signed or unsigned integer.".format(1, 2**64))]
681    ], error.filter_errors(constraints.check_constraints(ir)))
682
683  def test_variable_expression_out_of_range_for_int64(self):
684    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
685                           "struct Foo:\n"
686                           "  0  [+8]   UInt  x\n"
687                           "  if x - 0x8000_0000_0000_0001 < 0:\n"
688                           "    8 [+1]   UInt  y\n")
689    condition = ir.module[0].type[0].structure.field[1].existence_condition
690    error_location = condition.function.args[0].source_location
691    self.assertEqual([
692        [error.error(
693            "m.emb", error_location,
694            "Potential range of expression is {} to {}, which cannot fit in a "
695            "64-bit signed or unsigned integer.".format(-(2**63) - 1,
696                                                        2**63 - 2))]
697    ], error.filter_errors(constraints.check_constraints(ir)))
698
699  def test_requires_expression_out_of_range_for_uint64(self):
700    ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n'
701                           "struct Foo:\n"
702                           "  0  [+8]   UInt  x\n"
703                           "    [requires: this * 2 < 0x1_0000]\n")
704    attribute_list = ir.module[0].type[0].structure.field[0].attribute
705    error_arg = attribute_list[0].value.expression.function.args[0]
706    error_location = error_arg.source_location
707    self.assertEqual(
708        [[
709            error.error(
710                "m.emb", error_location,
711                "Potential range of expression is {} to {}, which cannot fit "
712                "in a 64-bit signed or unsigned integer.".format(0, 2**65-2))
713        ]],
714        error.filter_errors(constraints.check_constraints(ir)))
715
716  def test_arguments_require_different_signedness_64_bits(self):
717    ir = _make_ir_from_emb(
718        '[$default byte_order: "LittleEndian"]\n'
719        "struct Foo:\n"
720        "  0 [+1]    UInt  x\n"
721        # Left side requires uint64, right side requires int64.
722        "  if (x + 0x8000_0000_0000_0000) + (x - 0x7fff_ffff_ffff_ffff) < 10:\n"
723        "    1 [+1]  UInt  y\n")
724    condition = ir.module[0].type[0].structure.field[1].existence_condition
725    error_expression = condition.function.args[0]
726    error_location = error_expression.source_location
727    arg0_location = error_expression.function.args[0].source_location
728    arg1_location = error_expression.function.args[1].source_location
729    self.assertEqual([
730        [error.error(
731            "m.emb", error_location,
732            "Either all arguments to '+' and its result must fit in a 64-bit "
733            "unsigned integer, or all must fit in a 64-bit signed integer."),
734         error.note("m.emb", arg0_location,
735                    "Requires unsigned 64-bit integer."),
736         error.note("m.emb", arg1_location,
737                    "Requires signed 64-bit integer.")]
738    ], error.filter_errors(constraints.check_constraints(ir)))
739
740  def test_return_value_requires_different_signedness_from_arguments(self):
741    ir = _make_ir_from_emb(
742        '[$default byte_order: "LittleEndian"]\n'
743        "struct Foo:\n"
744        "  0 [+1]    UInt  x\n"
745        # Both arguments require uint64; result fits in int64.
746        "  if (x + 0x7fff_ffff_ffff_ffff) - 0x8000_0000_0000_0000 < 10:\n"
747        "    1 [+1]  UInt  y\n")
748    condition = ir.module[0].type[0].structure.field[1].existence_condition
749    error_expression = condition.function.args[0]
750    error_location = error_expression.source_location
751    arg0_location = error_expression.function.args[0].source_location
752    arg1_location = error_expression.function.args[1].source_location
753    self.assertEqual([
754        [error.error(
755            "m.emb", error_location,
756            "Either all arguments to '-' and its result must fit in a 64-bit "
757            "unsigned integer, or all must fit in a 64-bit signed integer."),
758         error.note("m.emb", arg0_location,
759                    "Requires unsigned 64-bit integer."),
760         error.note("m.emb", arg1_location,
761                    "Requires unsigned 64-bit integer."),
762         error.note("m.emb", error_location,
763                    "Requires signed 64-bit integer.")]
764    ], error.filter_errors(constraints.check_constraints(ir)))
765
766  def test_return_value_requires_different_signedness_from_one_argument(self):
767    ir = _make_ir_from_emb(
768        '[$default byte_order: "LittleEndian"]\n'
769        "struct Foo:\n"
770        "  0 [+1]    UInt  x\n"
771        # One argument requires uint64; result fits in int64.
772        "  if (x + 0x7fff_ffff_ffff_fff0) - 0x7fff_ffff_ffff_ffff < 10:\n"
773        "    1 [+1]  UInt  y\n")
774    condition = ir.module[0].type[0].structure.field[1].existence_condition
775    error_expression = condition.function.args[0]
776    error_location = error_expression.source_location
777    arg0_location = error_expression.function.args[0].source_location
778    self.assertEqual([
779        [error.error(
780            "m.emb", error_location,
781            "Either all arguments to '-' and its result must fit in a 64-bit "
782            "unsigned integer, or all must fit in a 64-bit signed integer."),
783         error.note("m.emb", arg0_location,
784                    "Requires unsigned 64-bit integer."),
785         error.note("m.emb", error_location,
786                    "Requires signed 64-bit integer.")]
787    ], error.filter_errors(constraints.check_constraints(ir)))
788
789  def test_checks_constancy_of_constant_references(self):
790    ir = _make_ir_from_emb("struct Foo:\n"
791                           "  0 [+1]  UInt  x\n"
792                           "  let y = x\n"
793                           "  let z = Foo.y\n")
794    error_expression = ir.module[0].type[0].structure.field[2].read_transform
795    error_location = error_expression.source_location
796    note_field = ir.module[0].type[0].structure.field[1]
797    note_location = note_field.source_location
798    self.assertEqual([
799        [error.error("m.emb", error_location,
800                     "Static references must refer to constants."),
801         error.note("m.emb", note_location, "y is not constant.")]
802    ], error.filter_errors(constraints.check_constraints(ir)))
803
804  def test_checks_for_explicit_size_on_parameters(self):
805    ir = _make_ir_from_emb("struct Foo(y: UInt):\n"
806                           "  0 [+1]  UInt  x\n")
807    error_parameter = ir.module[0].type[0].runtime_parameter[0]
808    error_location = error_parameter.physical_type_alias.source_location
809    self.assertEqual(
810        [[error.error("m.emb", error_location,
811                      "Integer range of parameter must not be unbounded; it "
812                      "must fit in a 64-bit signed or unsigned integer.")]],
813        error.filter_errors(constraints.check_constraints(ir)))
814
815  def test_checks_for_correct_explicit_size_on_parameters(self):
816    ir = _make_ir_from_emb("struct Foo(y: UInt:300):\n"
817                           "  0 [+1]  UInt  x\n")
818    error_parameter = ir.module[0].type[0].runtime_parameter[0]
819    error_location = error_parameter.physical_type_alias.source_location
820    self.assertEqual(
821        [[error.error("m.emb", error_location,
822                      "Potential range of parameter is 0 to {}, which cannot "
823                      "fit in a 64-bit signed or unsigned integer.".format(
824                          2**300-1))]],
825        error.filter_errors(constraints.check_constraints(ir)))
826
827  def test_checks_for_explicit_enum_size_on_parameters(self):
828    ir = _make_ir_from_emb("struct Foo(y: Bar:8):\n"
829                           "  0 [+1]  UInt  x\n"
830                           "enum Bar:\n"
831                           "  QUX = 1\n")
832    error_parameter = ir.module[0].type[0].runtime_parameter[0]
833    error_size = error_parameter.physical_type_alias.size_in_bits
834    error_location = error_size.source_location
835    self.assertEqual(
836        [[error.error(
837            "m.emb", error_location,
838            "Parameters with enum type may not have explicit size.")]],
839        error.filter_errors(constraints.check_constraints(ir)))
840
841
842if __name__ == "__main__":
843  unittest.main()
844