# Copyright 2019 Google LLC # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # https://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for constraints.py.""" import unittest from compiler.front_end import attributes from compiler.front_end import constraints from compiler.front_end import glue from compiler.util import error from compiler.util import ir_data_utils from compiler.util import ir_util from compiler.util import test_util def _make_ir_from_emb(emb_text, name="m.emb"): ir, unused_debug_info, errors = glue.parse_emboss_file( name, test_util.dict_file_reader({name: emb_text}), stop_before_step="check_constraints") assert not errors, repr(errors) return ir class ConstraintsTest(unittest.TestCase): """Tests constraints.check_constraints and helpers.""" def test_error_on_missing_inner_array_size(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+1] UInt:8[][1] one_byte\n") # There is a latent issue here where the source location reported in this # error is using a default value of 0:0. An issue is filed at # https://github.com/google/emboss/issues/153 for further investigation. # In the meantime we use `ir_data_utils.reader` to mimic this legacy # behavior. error_array = ir_data_utils.reader( ir.module[0].type[0].structure.field[0].type.array_type) self.assertEqual([[ error.error( "m.emb", error_array.base_type.array_type.element_count.source_location, "Array dimensions can only be omitted for the outermost dimension.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_no_error_on_ok_array_size(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+1] UInt:8[1][1] one_byte\n") self.assertEqual([], constraints.check_constraints(ir)) def test_no_error_on_ok_missing_outer_array_size(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+1] UInt:8[1][] one_byte\n") self.assertEqual([], constraints.check_constraints(ir)) def test_no_error_on_dynamically_sized_struct_in_dynamically_sized_field( self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+1] UInt size\n" " 1 [+size] Bar bar\n" "struct Bar:\n" " 0 [+1] UInt size\n" " 1 [+size] UInt:8[] payload\n") self.assertEqual([], constraints.check_constraints(ir)) def test_no_error_on_dynamically_sized_struct_in_statically_sized_field(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+10] Bar bar\n" "struct Bar:\n" " 0 [+1] UInt size\n" " 1 [+size] UInt:8[] payload\n") self.assertEqual([], constraints.check_constraints(ir)) def test_no_error_non_fixed_size_outer_array_dimension(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+1] UInt size\n" " 1 [+size] UInt:8[1][size-1] one_byte\n") self.assertEqual([], constraints.check_constraints(ir)) def test_error_non_fixed_size_inner_array_dimension(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+1] UInt size\n" " 1 [+size] UInt:8[size-1][1] one_byte\n") error_array = ir.module[0].type[0].structure.field[1].type.array_type self.assertEqual([[ error.error( "m.emb", error_array.base_type.array_type.element_count.source_location, "Inner array dimensions must be constant.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_error_non_constant_inner_array_dimensions(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+1] Bar[1] one_byte\n" # There is no dynamically-sized byte-oriented type in # the Prelude, so this test has to make its own. "external Bar:\n" " [is_integer: true]\n" " [addressable_unit_size: 8]\n") error_array = ir.module[0].type[0].structure.field[0].type.array_type self.assertEqual([[ error.error( "m.emb", error_array.base_type.atomic_type.source_location, "Array elements must be fixed size.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_error_dynamically_sized_array_elements(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+1] Bar[1] bar\n" "struct Bar:\n" " 0 [+1] UInt size\n" " 1 [+size] UInt:8[] payload\n") error_array = ir.module[0].type[0].structure.field[0].type.array_type self.assertEqual([[ error.error( "m.emb", error_array.base_type.atomic_type.source_location, "Array elements must be fixed size.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_field_too_small_for_type(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+1] Bar bar\n" "struct Bar:\n" " 0 [+2] UInt value\n") error_type = ir.module[0].type[0].structure.field[0].type self.assertEqual([[ error.error( "m.emb", error_type.source_location, "Fixed-size type 'Bar' cannot be placed in field of size 8 bits; " "requires 16 bits.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_dynamically_sized_field_always_too_small_for_type(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+1] bits:\n" " 0 [+1] UInt x\n" " 0 [+x] Bar bar\n" "struct Bar:\n" " 0 [+2] UInt value\n") error_type = ir.module[0].type[0].structure.field[2].type self.assertEqual([[ error.error( "m.emb", error_type.source_location, "Field of maximum size 8 bits cannot hold fixed-size type 'Bar', " "which requires 16 bits.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_struct_field_too_big_for_type(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+2] Byte double_byte\n" "struct Byte:\n" " 0 [+1] UInt b\n") error_type = ir.module[0].type[0].structure.field[0].type self.assertEqual([[ error.error( "m.emb", error_type.source_location, "Fixed-size type 'Byte' cannot be placed in field of size 16 bits; " "requires 8 bits.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_bits_field_too_big_for_type(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+9] UInt uint72\n" ' [byte_order: "LittleEndian"]\n') error_field = ir.module[0].type[0].structure.field[0] uint_type = ir_util.find_object(error_field.type.atomic_type.reference, ir) uint_requirements = ir_util.get_attribute(uint_type.attribute, attributes.STATIC_REQUIREMENTS) self.assertEqual([[ error.error("m.emb", error_field.source_location, "Requirements of UInt not met."), error.note("", uint_requirements.source_location, "Requirements specified here."), ]], error.filter_errors(constraints.check_constraints(ir))) def test_field_type_not_allowed_in_bits(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "bits Foo:\n" " 0 [+16] Bar bar\n" "external Bar:\n" " [addressable_unit_size: 8]\n") error_type = ir.module[0].type[0].structure.field[0].type self.assertEqual([[ error.error( "m.emb", error_type.source_location, "Byte-oriented type 'Bar' cannot be used in a bits field.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_arrays_allowed_in_bits(self): ir = _make_ir_from_emb("bits Foo:\n" " 0 [+16] Flag[16] bar\n") self.assertEqual([], constraints.check_constraints(ir)) def test_oversized_anonymous_bit_field(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+4] bits:\n" " 0 [+8] UInt field\n") self.assertEqual([], constraints.check_constraints(ir)) def test_undersized_anonymous_bit_field(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+1] bits:\n" " 0 [+32] UInt field\n") error_type = ir.module[0].type[0].structure.field[0].type self.assertEqual([[ error.error( "m.emb", error_type.source_location, "Fixed-size anonymous type cannot be placed in field of size 8 " "bits; requires 32 bits.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_reserved_field_name(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+8] UInt restrict\n") error_name = ir.module[0].type[0].structure.field[0].name.name self.assertEqual([[ error.error( "m.emb", error_name.source_location, "C reserved word may not be used as a field name.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_reserved_type_name(self): ir = _make_ir_from_emb("struct False:\n" " 0 [+1] UInt foo\n") error_name = ir.module[0].type[0].name.name self.assertEqual([[ error.error( "m.emb", error_name.source_location, "Python 3 reserved word may not be used as a type name.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_reserved_enum_name(self): ir = _make_ir_from_emb("enum Foo:\n" " NULL = 1\n") error_name = ir.module[0].type[0].enumeration.value[0].name.name self.assertEqual([[ error.error( "m.emb", error_name.source_location, "C reserved word may not be used as an enum name.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_bits_type_in_struct_array(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+10] UInt:8[10] array\n") self.assertEqual([], constraints.check_constraints(ir)) def test_bits_type_in_bits_array(self): ir = _make_ir_from_emb("bits Foo:\n" " 0 [+10] UInt:8[10] array\n") self.assertEqual([], constraints.check_constraints(ir)) def test_explicit_size_too_small(self): ir = _make_ir_from_emb("bits Foo:\n" " 0 [+0] UInt:0 zero_bit\n") error_field = ir.module[0].type[0].structure.field[0] uint_type = ir_util.find_object(error_field.type.atomic_type.reference, ir) uint_requirements = ir_util.get_attribute(uint_type.attribute, attributes.STATIC_REQUIREMENTS) self.assertEqual([[ error.error("m.emb", error_field.source_location, "Requirements of UInt not met."), error.note("", uint_requirements.source_location, "Requirements specified here."), ]], error.filter_errors(constraints.check_constraints(ir))) def test_explicit_enumeration_size_too_small(self): ir = _make_ir_from_emb('[$default byte_order: "BigEndian"]\n' "bits Foo:\n" " 0 [+0] Bar:0 zero_bit\n" "enum Bar:\n" " BAZ = 0\n") error_type = ir.module[0].type[0].structure.field[0].type self.assertEqual([[ error.error("m.emb", error_type.source_location, "Enumeration type 'Bar' cannot be 0 bits; type 'Bar' " "must be between 1 and 64 bits, inclusive."), ]], error.filter_errors(constraints.check_constraints(ir))) def test_explicit_size_too_big_for_field(self): ir = _make_ir_from_emb("bits Foo:\n" " 0 [+8] UInt:32 thirty_two_bit\n") error_type = ir.module[0].type[0].structure.field[0].type self.assertEqual([[ error.error( "m.emb", error_type.source_location, "Fixed-size type 'UInt:32' cannot be placed in field of size 8 " "bits; requires 32 bits.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_explicit_size_too_small_for_field(self): ir = _make_ir_from_emb("bits Foo:\n" " 0 [+64] UInt:32 thirty_two_bit\n") error_type = ir.module[0].type[0].structure.field[0].type self.assertEqual([[ error.error("m.emb", error_type.source_location, "Fixed-size type 'UInt:32' cannot be placed in field of " "size 64 bits; requires 32 bits.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_explicit_size_too_big(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+16] UInt:128 one_twenty_eight_bit\n" ' [byte_order: "LittleEndian"]\n') error_field = ir.module[0].type[0].structure.field[0] uint_type = ir_util.find_object(error_field.type.atomic_type.reference, ir) uint_requirements = ir_util.get_attribute(uint_type.attribute, attributes.STATIC_REQUIREMENTS) self.assertEqual([[ error.error("m.emb", error_field.source_location, "Requirements of UInt not met."), error.note("", uint_requirements.source_location, "Requirements specified here."), ]], error.filter_errors(constraints.check_constraints(ir))) def test_explicit_enumeration_size_too_big(self): ir = _make_ir_from_emb('[$default byte_order: "BigEndian"]\n' "struct Foo:\n" " 0 [+9] Bar seventy_two_bit\n" "enum Bar:\n" " BAZ = 0\n") error_type = ir.module[0].type[0].structure.field[0].type self.assertEqual([[ error.error("m.emb", error_type.source_location, "Enumeration type 'Bar' cannot be 72 bits; type 'Bar' " + "must be between 1 and 64 bits, inclusive."), ]], error.filter_errors(constraints.check_constraints(ir))) def test_explicit_enumeration_size_too_big_for_small_enum(self): ir = _make_ir_from_emb('[$default byte_order: "BigEndian"]\n' "struct Foo:\n" " 0 [+8] Bar sixty_four_bit\n" "enum Bar:\n" " [maximum_bits: 63]\n" " BAZ = 0\n") error_type = ir.module[0].type[0].structure.field[0].type self.assertEqual([[ error.error("m.emb", error_type.source_location, "Enumeration type 'Bar' cannot be 64 bits; type 'Bar' " + "must be between 1 and 63 bits, inclusive."), ]], error.filter_errors(constraints.check_constraints(ir))) def test_explicit_size_on_fixed_size_type(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+1] Byte:8 one_byte\n" "struct Byte:\n" " 0 [+1] UInt b\n") self.assertEqual([], constraints.check_constraints(ir)) def test_explicit_size_too_small_on_fixed_size_type(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+0] Byte:0 null_byte\n" "struct Byte:\n" " 0 [+1] UInt b\n") error_type = ir.module[0].type[0].structure.field[0].type self.assertEqual([[ error.error( "m.emb", error_type.size_in_bits.source_location, "Explicit size of 0 bits does not match fixed size (8 bits) of " "type 'Byte'."), error.note("m.emb", ir.module[0].type[1].source_location, "Size specified here."), ]], error.filter_errors(constraints.check_constraints(ir))) def test_explicit_size_too_big_on_fixed_size_type(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+2] Byte:16 double_byte\n" "struct Byte:\n" " 0 [+1] UInt b\n") error_type = ir.module[0].type[0].structure.field[0].type self.assertEqual([[ error.error( "m.emb", error_type.size_in_bits.source_location, "Explicit size of 16 bits does not match fixed size (8 bits) of " "type 'Byte'."), error.note( "m.emb", ir.module[0].type[1].source_location, "Size specified here."), ]], error.filter_errors(constraints.check_constraints(ir))) def test_explicit_size_ignored_on_variable_size_type(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+1] UInt n\n" " 1 [+n] UInt:8[] d\n" "struct Bar:\n" " 0 [+10] Foo:80 foo\n") self.assertEqual([], constraints.check_constraints(ir)) def test_fixed_size_type_in_dynamically_sized_field(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+1] UInt bar\n" " 0 [+bar] Byte one_byte\n" "struct Byte:\n" " 0 [+1] UInt b\n") self.assertEqual([], constraints.check_constraints(ir)) def test_enum_in_dynamically_sized_field(self): ir = _make_ir_from_emb('[$default byte_order: "BigEndian"]\n' "struct Foo:\n" " 0 [+1] UInt bar\n" " 0 [+bar] Baz baz\n" "enum Baz:\n" " QUX = 0\n") error_type = ir.module[0].type[0].structure.field[1].type self.assertEqual( [[ error.error("m.emb", error_type.source_location, "Enumeration type 'Baz' cannot be placed in a " "dynamically-sized field.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_enum_value_too_high(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "enum Foo:\n" " HIGH = 0x1_0000_0000_0000_0000\n") error_value = ir.module[0].type[0].enumeration.value[0].value self.assertEqual([ [error.error( "m.emb", error_value.source_location, # TODO(bolms): Try to print numbers like 2**64 in hex? (I.e., if a # number is a round number in hex, but not in decimal, print in # hex?) "Value 18446744073709551616 is out of range for 64-bit unsigned " + "enumeration.")] ], constraints.check_constraints(ir)) def test_enum_value_too_low(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "enum Foo:\n" " LOW = -0x8000_0000_0000_0001\n") error_value = ir.module[0].type[0].enumeration.value[0].value self.assertEqual([ [error.error( "m.emb", error_value.source_location, "Value -9223372036854775809 is out of range for 64-bit signed " + "enumeration.")] ], constraints.check_constraints(ir)) def test_enum_value_too_wide(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "enum Foo:\n" " LOW = -1\n" " HIGH = 0x8000_0000_0000_0000\n") error_value = ir.module[0].type[0].enumeration.value[1].value self.assertEqual([[ error.error( "m.emb", error_value.source_location, "Value 9223372036854775808 is out of range for 64-bit signed " + "enumeration.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_enum_value_too_wide_unsigned_error_message(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "enum Foo:\n" " LOW = -2\n" " LOW2 = -1\n" " HIGH = 0x8000_0000_0000_0000\n") error_value = ir.module[0].type[0].enumeration.value[2].value self.assertEqual([[ error.error( "m.emb", error_value.source_location, "Value 9223372036854775808 is out of range for 64-bit signed " + "enumeration.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_enum_value_too_wide_small_size_error_message(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "enum Foo:\n" " [maximum_bits: 8]\n" " HIGH = 0x100\n") error_value = ir.module[0].type[0].enumeration.value[0].value self.assertEqual([[ error.error( "m.emb", error_value.source_location, "Value 256 is out of range for 8-bit unsigned enumeration.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_enum_value_too_wide_small_size_signed_error_message(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "enum Foo:\n" " [maximum_bits: 8]\n" " [is_signed: true]\n" " HIGH = 0x80\n") error_value = ir.module[0].type[0].enumeration.value[0].value self.assertEqual([[ error.error( "m.emb", error_value.source_location, "Value 128 is out of range for 8-bit signed enumeration.") ]], error.filter_errors(constraints.check_constraints(ir))) def test_enum_value_too_wide_multiple(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "enum Foo:\n" " LOW = -2\n" " LOW2 = -1\n" " HIGH = 0x8000_0000_0000_0000\n" " HIGH2 = 0x8000_0000_0000_0001\n") error_value = ir.module[0].type[0].enumeration.value[2].value error_value2 = ir.module[0].type[0].enumeration.value[3].value self.assertEqual([ [error.error( "m.emb", error_value.source_location, "Value 9223372036854775808 is out of range for 64-bit signed " + "enumeration.")], [error.error( "m.emb", error_value2.source_location, "Value 9223372036854775809 is out of range for 64-bit signed " + "enumeration.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_enum_value_too_wide_multiple_signed_error_message(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "enum Foo:\n" " LOW = -3\n" " LOW2 = -2\n" " LOW3 = -1\n" " HIGH = 0x8000_0000_0000_0000\n" " HIGH2 = 0x8000_0000_0000_0001\n") error_value = ir.module[0].type[0].enumeration.value[3].value error_value2 = ir.module[0].type[0].enumeration.value[4].value self.assertEqual([ [error.error( "m.emb", error_value.source_location, "Value 9223372036854775808 is out of range for 64-bit signed " "enumeration.")], [error.error( "m.emb", error_value2.source_location, "Value 9223372036854775809 is out of range for 64-bit signed " "enumeration.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_enum_value_mixed_error_message(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "enum Foo:\n" " LOW = -1\n" " HIGH = 0x8000_0000_0000_0000\n" " HIGH2 = 0x1_0000_0000_0000_0000\n") error_value1 = ir.module[0].type[0].enumeration.value[1].value error_value2 = ir.module[0].type[0].enumeration.value[2].value self.assertEqual([ [error.error( "m.emb", error_value1.source_location, "Value 9223372036854775808 is out of range for 64-bit signed " + "enumeration.")], [error.error( "m.emb", error_value2.source_location, "Value 18446744073709551616 is out of range for 64-bit signed " + "enumeration.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_enum_value_explicitly_signed_error_message(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "enum Foo:\n" " [is_signed: true]\n" " HIGH = 0x8000_0000_0000_0000\n" " HIGH2 = 0x1_0000_0000_0000_0000\n") error_value0 = ir.module[0].type[0].enumeration.value[0].value error_value1 = ir.module[0].type[0].enumeration.value[1].value self.assertEqual([ [error.error( "m.emb", error_value0.source_location, "Value 9223372036854775808 is out of range for 64-bit signed " + "enumeration.")], [error.error( "m.emb", error_value1.source_location, "Value 18446744073709551616 is out of range for 64-bit signed " + "enumeration.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_enum_value_explicitly_unsigned_error_message(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "enum Foo:\n" " [is_signed: false]\n" " LOW = -1\n" " HIGH = 0x8000_0000_0000_0000\n" " HIGH2 = 0x1_0000_0000_0000_0000\n") error_value0 = ir.module[0].type[0].enumeration.value[0].value error_value2 = ir.module[0].type[0].enumeration.value[2].value self.assertEqual([ [error.error( "m.emb", error_value0.source_location, "Value -1 is out of range for 64-bit unsigned enumeration.")], [error.error( "m.emb", error_value2.source_location, "Value 18446744073709551616 is out of range for 64-bit unsigned " + "enumeration.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_explicit_non_byte_size_array_element(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+2] UInt:4[4] nibbles\n") error_type = ir.module[0].type[0].structure.field[0].type.array_type self.assertEqual([ [error.error( "m.emb", error_type.base_type.source_location, "Array elements in structs must have sizes which are a multiple of " "8 bits.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_implicit_non_byte_size_array_element(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "bits Nibble:\n" " 0 [+4] UInt nibble\n" "struct Foo:\n" " 0 [+2] Nibble[4] nibbles\n") error_type = ir.module[0].type[1].structure.field[0].type.array_type self.assertEqual([ [error.error( "m.emb", error_type.base_type.source_location, "Array elements in structs must have sizes which are a multiple of " "8 bits.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_bits_must_be_fixed_size(self): ir = _make_ir_from_emb("bits Dynamic:\n" " 0 [+3] UInt x\n" " 3 [+3 * x] UInt:3[x] a\n") error_type = ir.module[0].type[0] self.assertEqual([ [error.error("m.emb", error_type.source_location, "`bits` types must be fixed size.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_bits_must_be_small(self): ir = _make_ir_from_emb("bits Big:\n" " 0 [+64] UInt x\n" " 64 [+1] UInt y\n") error_type = ir.module[0].type[0] self.assertEqual([ [error.error("m.emb", error_type.source_location, "`bits` types must be 64 bits or smaller.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_constant_expressions_must_be_small(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+8] UInt x\n" " if x < 0x1_0000_0000_0000_0000:\n" " 8 [+1] UInt y\n") condition = ir.module[0].type[0].structure.field[1].existence_condition error_location = condition.function.args[1].source_location self.assertEqual([ [error.error( "m.emb", error_location, "Constant value {} of expression cannot fit in a 64-bit signed or " "unsigned integer.".format(2**64))] ], error.filter_errors(constraints.check_constraints(ir))) def test_variable_expression_out_of_range_for_uint64(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+8] UInt x\n" " if x + 1 < 0xffff_ffff_ffff_ffff:\n" " 8 [+1] UInt y\n") condition = ir.module[0].type[0].structure.field[1].existence_condition error_location = condition.function.args[0].source_location self.assertEqual([ [error.error( "m.emb", error_location, "Potential range of expression is {} to {}, which cannot fit in a " "64-bit signed or unsigned integer.".format(1, 2**64))] ], error.filter_errors(constraints.check_constraints(ir))) def test_variable_expression_out_of_range_for_int64(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+8] UInt x\n" " if x - 0x8000_0000_0000_0001 < 0:\n" " 8 [+1] UInt y\n") condition = ir.module[0].type[0].structure.field[1].existence_condition error_location = condition.function.args[0].source_location self.assertEqual([ [error.error( "m.emb", error_location, "Potential range of expression is {} to {}, which cannot fit in a " "64-bit signed or unsigned integer.".format(-(2**63) - 1, 2**63 - 2))] ], error.filter_errors(constraints.check_constraints(ir))) def test_requires_expression_out_of_range_for_uint64(self): ir = _make_ir_from_emb('[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+8] UInt x\n" " [requires: this * 2 < 0x1_0000]\n") attribute_list = ir.module[0].type[0].structure.field[0].attribute error_arg = attribute_list[0].value.expression.function.args[0] error_location = error_arg.source_location self.assertEqual( [[ error.error( "m.emb", error_location, "Potential range of expression is {} to {}, which cannot fit " "in a 64-bit signed or unsigned integer.".format(0, 2**65-2)) ]], error.filter_errors(constraints.check_constraints(ir))) def test_arguments_require_different_signedness_64_bits(self): ir = _make_ir_from_emb( '[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+1] UInt x\n" # Left side requires uint64, right side requires int64. " if (x + 0x8000_0000_0000_0000) + (x - 0x7fff_ffff_ffff_ffff) < 10:\n" " 1 [+1] UInt y\n") condition = ir.module[0].type[0].structure.field[1].existence_condition error_expression = condition.function.args[0] error_location = error_expression.source_location arg0_location = error_expression.function.args[0].source_location arg1_location = error_expression.function.args[1].source_location self.assertEqual([ [error.error( "m.emb", error_location, "Either all arguments to '+' and its result must fit in a 64-bit " "unsigned integer, or all must fit in a 64-bit signed integer."), error.note("m.emb", arg0_location, "Requires unsigned 64-bit integer."), error.note("m.emb", arg1_location, "Requires signed 64-bit integer.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_return_value_requires_different_signedness_from_arguments(self): ir = _make_ir_from_emb( '[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+1] UInt x\n" # Both arguments require uint64; result fits in int64. " if (x + 0x7fff_ffff_ffff_ffff) - 0x8000_0000_0000_0000 < 10:\n" " 1 [+1] UInt y\n") condition = ir.module[0].type[0].structure.field[1].existence_condition error_expression = condition.function.args[0] error_location = error_expression.source_location arg0_location = error_expression.function.args[0].source_location arg1_location = error_expression.function.args[1].source_location self.assertEqual([ [error.error( "m.emb", error_location, "Either all arguments to '-' and its result must fit in a 64-bit " "unsigned integer, or all must fit in a 64-bit signed integer."), error.note("m.emb", arg0_location, "Requires unsigned 64-bit integer."), error.note("m.emb", arg1_location, "Requires unsigned 64-bit integer."), error.note("m.emb", error_location, "Requires signed 64-bit integer.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_return_value_requires_different_signedness_from_one_argument(self): ir = _make_ir_from_emb( '[$default byte_order: "LittleEndian"]\n' "struct Foo:\n" " 0 [+1] UInt x\n" # One argument requires uint64; result fits in int64. " if (x + 0x7fff_ffff_ffff_fff0) - 0x7fff_ffff_ffff_ffff < 10:\n" " 1 [+1] UInt y\n") condition = ir.module[0].type[0].structure.field[1].existence_condition error_expression = condition.function.args[0] error_location = error_expression.source_location arg0_location = error_expression.function.args[0].source_location self.assertEqual([ [error.error( "m.emb", error_location, "Either all arguments to '-' and its result must fit in a 64-bit " "unsigned integer, or all must fit in a 64-bit signed integer."), error.note("m.emb", arg0_location, "Requires unsigned 64-bit integer."), error.note("m.emb", error_location, "Requires signed 64-bit integer.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_checks_constancy_of_constant_references(self): ir = _make_ir_from_emb("struct Foo:\n" " 0 [+1] UInt x\n" " let y = x\n" " let z = Foo.y\n") error_expression = ir.module[0].type[0].structure.field[2].read_transform error_location = error_expression.source_location note_field = ir.module[0].type[0].structure.field[1] note_location = note_field.source_location self.assertEqual([ [error.error("m.emb", error_location, "Static references must refer to constants."), error.note("m.emb", note_location, "y is not constant.")] ], error.filter_errors(constraints.check_constraints(ir))) def test_checks_for_explicit_size_on_parameters(self): ir = _make_ir_from_emb("struct Foo(y: UInt):\n" " 0 [+1] UInt x\n") error_parameter = ir.module[0].type[0].runtime_parameter[0] error_location = error_parameter.physical_type_alias.source_location self.assertEqual( [[error.error("m.emb", error_location, "Integer range of parameter must not be unbounded; it " "must fit in a 64-bit signed or unsigned integer.")]], error.filter_errors(constraints.check_constraints(ir))) def test_checks_for_correct_explicit_size_on_parameters(self): ir = _make_ir_from_emb("struct Foo(y: UInt:300):\n" " 0 [+1] UInt x\n") error_parameter = ir.module[0].type[0].runtime_parameter[0] error_location = error_parameter.physical_type_alias.source_location self.assertEqual( [[error.error("m.emb", error_location, "Potential range of parameter is 0 to {}, which cannot " "fit in a 64-bit signed or unsigned integer.".format( 2**300-1))]], error.filter_errors(constraints.check_constraints(ir))) def test_checks_for_explicit_enum_size_on_parameters(self): ir = _make_ir_from_emb("struct Foo(y: Bar:8):\n" " 0 [+1] UInt x\n" "enum Bar:\n" " QUX = 1\n") error_parameter = ir.module[0].type[0].runtime_parameter[0] error_size = error_parameter.physical_type_alias.size_in_bits error_location = error_size.source_location self.assertEqual( [[error.error( "m.emb", error_location, "Parameters with enum type may not have explicit size.")]], error.filter_errors(constraints.check_constraints(ir))) if __name__ == "__main__": unittest.main()