Reorganize the source tree.

Primarily, this moves front_end/ and back_end/ into compiler/, and
breaks up public/.

Top level directories are now:

compiler/ -- All compiler source code
runtime/ -- Runtime libraries
doc/ -- Documentation
testdata/ -- Test .embs and related files
integration/ -- Support for using Emboss with other tools

Additionally, there are:

embossc -- Standalone driver for the compiler
build_defs.bzl -- Bazel emboss_cc_library macro
README.md -- 1-pager that shows inline on github.com/google/emboss/

... plus some miscellaneous files that are required to be at the
repository root.
diff --git a/compiler/front_end/constraints_test.py b/compiler/front_end/constraints_test.py
new file mode 100644
index 0000000..ff33586
--- /dev/null
+++ b/compiler/front_end/constraints_test.py
@@ -0,0 +1,751 @@
+# 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.front_end import test_util
+from compiler.util import error
+from compiler.util import ir_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")
+    error_array = 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; enumerations "
+                    "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; enumerations "
+                    "must be between 1 and 64 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 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 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[0].value
+    self.assertEqual([[
+        error.error(
+            "m.emb", error_value.source_location,
+            "Value -1 is out of range for unsigned 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 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[0].value
+    error_value2 = ir.module[0].type[0].enumeration.value[1].value
+    self.assertEqual([
+        [error.error(
+            "m.emb", error_value.source_location,
+            "Value -2 is out of range for unsigned enumeration.")],
+        [error.error(
+            "m.emb", error_value2.source_location,
+            "Value -1 is out of range for unsigned 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 signed "
+            "enumeration.")],
+        [error.error(
+            "m.emb", error_value2.source_location,
+            "Value 9223372036854775809 is out of range for 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_value = 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_value.source_location,
+            "Value -1 is out of range for unsigned enumeration.")],
+        [error.error(
+            "m.emb", error_value2.source_location,
+            "Value 18446744073709551616 is out of range for 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()