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