Merge pull request #131 from EricRahm/use_enums_in_ir

Split out IR enums
diff --git a/compiler/back_end/cpp/header_generator.py b/compiler/back_end/cpp/header_generator.py
index a761bde..c671d6e 100644
--- a/compiler/back_end/cpp/header_generator.py
+++ b/compiler/back_end/cpp/header_generator.py
@@ -250,8 +250,8 @@
                                            buffer_type, byte_order,
                                            parent_addressable_unit):
   """Returns the adapted C++ type information needed to construct a view."""
-  if (parent_addressable_unit == ir_pb2.TypeDefinition.BYTE and
-      type_definition.addressable_unit == ir_pb2.TypeDefinition.BIT):
+  if (parent_addressable_unit == ir_pb2.AddressableUnit.BYTE and
+      type_definition.addressable_unit == ir_pb2.AddressableUnit.BIT):
     assert byte_order
     return _bytes_to_bits_convertor(buffer_type, byte_order, size_in_bits)
   else:
@@ -374,7 +374,7 @@
             element_view_parameter_types="".join(
                 ", " + p for p in element_view_parameter_types),
             element_size=element_size,
-            addressable_unit_size=parent_addressable_unit,
+            addressable_unit_size=int(parent_addressable_unit),
             buffer_type=buffer_type),
         element_view_parameter_types,
         element_view_parameters
@@ -412,19 +412,19 @@
 def _builtin_function_name(function):
   """Returns the C++ operator name corresponding to an Emboss operator."""
   functions = {
-      ir_pb2.Function.ADDITION: "Sum",
-      ir_pb2.Function.SUBTRACTION: "Difference",
-      ir_pb2.Function.MULTIPLICATION: "Product",
-      ir_pb2.Function.EQUALITY: "Equal",
-      ir_pb2.Function.INEQUALITY: "NotEqual",
-      ir_pb2.Function.AND: "And",
-      ir_pb2.Function.OR: "Or",
-      ir_pb2.Function.LESS: "LessThan",
-      ir_pb2.Function.LESS_OR_EQUAL: "LessThanOrEqual",
-      ir_pb2.Function.GREATER: "GreaterThan",
-      ir_pb2.Function.GREATER_OR_EQUAL: "GreaterThanOrEqual",
-      ir_pb2.Function.CHOICE: "Choice",
-      ir_pb2.Function.MAXIMUM: "Maximum",
+      ir_pb2.FunctionMapping.ADDITION: "Sum",
+      ir_pb2.FunctionMapping.SUBTRACTION: "Difference",
+      ir_pb2.FunctionMapping.MULTIPLICATION: "Product",
+      ir_pb2.FunctionMapping.EQUALITY: "Equal",
+      ir_pb2.FunctionMapping.INEQUALITY: "NotEqual",
+      ir_pb2.FunctionMapping.AND: "And",
+      ir_pb2.FunctionMapping.OR: "Or",
+      ir_pb2.FunctionMapping.LESS: "LessThan",
+      ir_pb2.FunctionMapping.LESS_OR_EQUAL: "LessThanOrEqual",
+      ir_pb2.FunctionMapping.GREATER: "GreaterThan",
+      ir_pb2.FunctionMapping.GREATER_OR_EQUAL: "GreaterThanOrEqual",
+      ir_pb2.FunctionMapping.CHOICE: "Choice",
+      ir_pb2.FunctionMapping.MAXIMUM: "Maximum",
   }
   return functions[function]
 
@@ -488,9 +488,9 @@
 def _render_builtin_operation(expression, ir, field_reader, subexpressions):
   """Renders a built-in operation (+, -, &&, etc.) into C++ code."""
   assert expression.function.function not in (
-      ir_pb2.Function.UPPER_BOUND, ir_pb2.Function.LOWER_BOUND), (
+      ir_pb2.FunctionMapping.UPPER_BOUND, ir_pb2.FunctionMapping.LOWER_BOUND), (
           "UPPER_BOUND and LOWER_BOUND should be constant.")
-  if expression.function.function == ir_pb2.Function.PRESENCE:
+  if expression.function.function == ir_pb2.FunctionMapping.PRESENCE:
     return field_reader.render_existence(expression.function.args[0],
                                          subexpressions)
   args = expression.function.args
diff --git a/compiler/front_end/attribute_checker.py b/compiler/front_end/attribute_checker.py
index cb8db74..b621081 100644
--- a/compiler/front_end/attribute_checker.py
+++ b/compiler/front_end/attribute_checker.py
@@ -218,7 +218,7 @@
       ir_util.get_base_type(field.type).atomic_type.reference.canonical_name,
       ir)
   assert field_type is not None
-  assert field_type.addressable_unit != ir_pb2.TypeDefinition.NONE
+  assert field_type.addressable_unit != ir_pb2.AddressableUnit.NONE
   return field_type.addressable_unit != type_definition.addressable_unit
 
 
@@ -279,9 +279,9 @@
   size = ir_util.get_integer_attribute(type_definition.attribute,
                                        attributes.ADDRESSABLE_UNIT_SIZE)
   if size == 1:
-    type_definition.addressable_unit = ir_pb2.TypeDefinition.BIT
+    type_definition.addressable_unit = ir_pb2.AddressableUnit.BIT
   elif size == 8:
-    type_definition.addressable_unit = ir_pb2.TypeDefinition.BYTE
+    type_definition.addressable_unit = ir_pb2.AddressableUnit.BYTE
   # If the addressable_unit_size is not in (1, 8), it will be caught by
   # _verify_addressable_unit_attribute_on_external, below.
 
diff --git a/compiler/front_end/attribute_checker_test.py b/compiler/front_end/attribute_checker_test.py
index 11a5c18..e54d277 100644
--- a/compiler/front_end/attribute_checker_test.py
+++ b/compiler/front_end/attribute_checker_test.py
@@ -550,14 +550,14 @@
     external_ir = _make_ir_from_emb("external Foo:\n"
                                     "  [addressable_unit_size: 1]\n")
     self.assertEqual([], attribute_checker.normalize_and_verify(external_ir))
-    self.assertEqual(ir_pb2.TypeDefinition.BIT,
+    self.assertEqual(ir_pb2.AddressableUnit.BIT,
                      external_ir.module[0].type[0].addressable_unit)
 
   def test_adds_byte_addressable_unit_to_external(self):
     external_ir = _make_ir_from_emb("external Foo:\n"
                                     "  [addressable_unit_size: 8]\n")
     self.assertEqual([], attribute_checker.normalize_and_verify(external_ir))
-    self.assertEqual(ir_pb2.TypeDefinition.BYTE,
+    self.assertEqual(ir_pb2.AddressableUnit.BYTE,
                      external_ir.module[0].type[0].addressable_unit)
 
   def test_rejects_requires_using_array(self):
diff --git a/compiler/front_end/constraints.py b/compiler/front_end/constraints.py
index b43ca0e..107b55e 100644
--- a/compiler/front_end/constraints.py
+++ b/compiler/front_end/constraints.py
@@ -105,7 +105,7 @@
       return
     base_type_size = fixed_size
   if base_type_size % type_definition.addressable_unit != 0:
-    assert type_definition.addressable_unit == ir_pb2.TypeDefinition.BYTE
+    assert type_definition.addressable_unit == ir_pb2.AddressableUnit.BYTE
     errors.append([error.error(source_file_name,
                                type_ir.base_type.source_location,
                                "Array elements in structs must have sizes "
@@ -352,9 +352,9 @@
       type_ir.atomic_type.reference, ir)
   if (type_definition.addressable_unit %
       referenced_type_definition.addressable_unit != 0):
-    assert type_definition.addressable_unit == ir_pb2.TypeDefinition.BIT
+    assert type_definition.addressable_unit == ir_pb2.AddressableUnit.BIT
     assert (referenced_type_definition.addressable_unit ==
-            ir_pb2.TypeDefinition.BYTE)
+            ir_pb2.AddressableUnit.BYTE)
     errors.append([
         error.error(source_file_name, type_ir.source_location,
                     "Byte-oriented {} cannot be used in a bits field.".format(
@@ -365,7 +365,7 @@
 def _check_size_of_bits(type_ir, type_definition, source_file_name, errors):
   """Checks that `bits` types are fixed size, less than 64 bits."""
   del type_ir  # Unused
-  if type_definition.addressable_unit != ir_pb2.TypeDefinition.BIT:
+  if type_definition.addressable_unit != ir_pb2.AddressableUnit.BIT:
     return
   fixed_size = ir_util.get_integer_attribute(
       type_definition.attribute, attributes.FIXED_SIZE)
diff --git a/compiler/front_end/expression_bounds.py b/compiler/front_end/expression_bounds.py
index 5d37e1c..2f8d969 100644
--- a/compiler/front_end/expression_bounds.py
+++ b/compiler/front_end/expression_bounds.py
@@ -85,22 +85,22 @@
   for arg in expression.function.args:
     compute_constraints_of_expression(arg, ir)
   op = expression.function.function
-  if op in (ir_pb2.Function.ADDITION, ir_pb2.Function.SUBTRACTION):
+  if op in (ir_pb2.FunctionMapping.ADDITION, ir_pb2.FunctionMapping.SUBTRACTION):
     _compute_constraints_of_additive_operator(expression)
-  elif op == ir_pb2.Function.MULTIPLICATION:
+  elif op == ir_pb2.FunctionMapping.MULTIPLICATION:
     _compute_constraints_of_multiplicative_operator(expression)
-  elif op in (ir_pb2.Function.EQUALITY, ir_pb2.Function.INEQUALITY,
-              ir_pb2.Function.LESS, ir_pb2.Function.LESS_OR_EQUAL,
-              ir_pb2.Function.GREATER, ir_pb2.Function.GREATER_OR_EQUAL,
-              ir_pb2.Function.AND, ir_pb2.Function.OR):
+  elif op in (ir_pb2.FunctionMapping.EQUALITY, ir_pb2.FunctionMapping.INEQUALITY,
+              ir_pb2.FunctionMapping.LESS, ir_pb2.FunctionMapping.LESS_OR_EQUAL,
+              ir_pb2.FunctionMapping.GREATER, ir_pb2.FunctionMapping.GREATER_OR_EQUAL,
+              ir_pb2.FunctionMapping.AND, ir_pb2.FunctionMapping.OR):
     _compute_constant_value_of_comparison_operator(expression)
-  elif op == ir_pb2.Function.CHOICE:
+  elif op == ir_pb2.FunctionMapping.CHOICE:
     _compute_constraints_of_choice_operator(expression)
-  elif op == ir_pb2.Function.MAXIMUM:
+  elif op == ir_pb2.FunctionMapping.MAXIMUM:
     _compute_constraints_of_maximum_function(expression)
-  elif op == ir_pb2.Function.PRESENCE:
+  elif op == ir_pb2.FunctionMapping.PRESENCE:
     _compute_constraints_of_existence_function(expression, ir)
-  elif op in (ir_pb2.Function.UPPER_BOUND, ir_pb2.Function.LOWER_BOUND):
+  elif op in (ir_pb2.FunctionMapping.UPPER_BOUND, ir_pb2.FunctionMapping.LOWER_BOUND):
     _compute_constraints_of_bound_function(expression)
   else:
     assert False, "Unknown operator {!r}".format(op)
@@ -317,8 +317,8 @@
 def _compute_constraints_of_additive_operator(expression):
   """Computes the modular value of an additive expression."""
   funcs = {
-      ir_pb2.Function.ADDITION: _add,
-      ir_pb2.Function.SUBTRACTION: _sub,
+      ir_pb2.FunctionMapping.ADDITION: _add,
+      ir_pb2.FunctionMapping.SUBTRACTION: _sub,
   }
   func = funcs[expression.function.function]
   args = expression.function.args
@@ -337,7 +337,7 @@
                                                 new_modulus)
   lmax = left.type.integer.maximum_value
   lmin = left.type.integer.minimum_value
-  if expression.function.function == ir_pb2.Function.SUBTRACTION:
+  if expression.function.function == ir_pb2.FunctionMapping.SUBTRACTION:
     rmax = right.type.integer.minimum_value
     rmin = right.type.integer.maximum_value
   else:
@@ -502,14 +502,14 @@
   args = expression.function.args
   if all(ir_util.is_constant(arg) for arg in args):
     functions = {
-        ir_pb2.Function.EQUALITY: operator.eq,
-        ir_pb2.Function.INEQUALITY: operator.ne,
-        ir_pb2.Function.LESS: operator.lt,
-        ir_pb2.Function.LESS_OR_EQUAL: operator.le,
-        ir_pb2.Function.GREATER: operator.gt,
-        ir_pb2.Function.GREATER_OR_EQUAL: operator.ge,
-        ir_pb2.Function.AND: operator.and_,
-        ir_pb2.Function.OR: operator.or_,
+        ir_pb2.FunctionMapping.EQUALITY: operator.eq,
+        ir_pb2.FunctionMapping.INEQUALITY: operator.ne,
+        ir_pb2.FunctionMapping.LESS: operator.lt,
+        ir_pb2.FunctionMapping.LESS_OR_EQUAL: operator.le,
+        ir_pb2.FunctionMapping.GREATER: operator.gt,
+        ir_pb2.FunctionMapping.GREATER_OR_EQUAL: operator.ge,
+        ir_pb2.FunctionMapping.AND: operator.and_,
+        ir_pb2.FunctionMapping.OR: operator.or_,
     }
     func = functions[expression.function.function]
     expression.type.boolean.value = func(
@@ -518,9 +518,9 @@
 
 def _compute_constraints_of_bound_function(expression):
   """Computes the constraints of $upper_bound or $lower_bound."""
-  if expression.function.function == ir_pb2.Function.UPPER_BOUND:
+  if expression.function.function == ir_pb2.FunctionMapping.UPPER_BOUND:
     value = expression.function.args[0].type.integer.maximum_value
-  elif expression.function.function == ir_pb2.Function.LOWER_BOUND:
+  elif expression.function.function == ir_pb2.FunctionMapping.LOWER_BOUND:
     value = expression.function.args[0].type.integer.minimum_value
   else:
     assert False, "Non-bound function"
diff --git a/compiler/front_end/module_ir.py b/compiler/front_end/module_ir.py
index 94b7d8b..52db843 100644
--- a/compiler/front_end/module_ir.py
+++ b/compiler/front_end/module_ir.py
@@ -192,17 +192,17 @@
 def _text_to_operator(text):
   """Converts an operator's textual name to its corresponding enum."""
   operations = {
-      '+': ir_pb2.Function.ADDITION,
-      '-': ir_pb2.Function.SUBTRACTION,
-      '*': ir_pb2.Function.MULTIPLICATION,
-      '==': ir_pb2.Function.EQUALITY,
-      '!=': ir_pb2.Function.INEQUALITY,
-      '&&': ir_pb2.Function.AND,
-      '||': ir_pb2.Function.OR,
-      '>': ir_pb2.Function.GREATER,
-      '>=': ir_pb2.Function.GREATER_OR_EQUAL,
-      '<': ir_pb2.Function.LESS,
-      '<=': ir_pb2.Function.LESS_OR_EQUAL,
+      '+': ir_pb2.FunctionMapping.ADDITION,
+      '-': ir_pb2.FunctionMapping.SUBTRACTION,
+      '*': ir_pb2.FunctionMapping.MULTIPLICATION,
+      '==': ir_pb2.FunctionMapping.EQUALITY,
+      '!=': ir_pb2.FunctionMapping.INEQUALITY,
+      '&&': ir_pb2.FunctionMapping.AND,
+      '||': ir_pb2.FunctionMapping.OR,
+      '>': ir_pb2.FunctionMapping.GREATER,
+      '>=': ir_pb2.FunctionMapping.GREATER_OR_EQUAL,
+      '<': ir_pb2.FunctionMapping.LESS,
+      '<=': ir_pb2.FunctionMapping.LESS_OR_EQUAL,
   }
   return operations[text]
 
@@ -210,10 +210,10 @@
 def _text_to_function(text):
   """Converts a function's textual name to its corresponding enum."""
   functions = {
-      '$max': ir_pb2.Function.MAXIMUM,
-      '$present': ir_pb2.Function.PRESENCE,
-      '$upper_bound': ir_pb2.Function.UPPER_BOUND,
-      '$lower_bound': ir_pb2.Function.LOWER_BOUND,
+      '$max': ir_pb2.FunctionMapping.MAXIMUM,
+      '$present': ir_pb2.FunctionMapping.PRESENCE,
+      '$upper_bound': ir_pb2.FunctionMapping.UPPER_BOUND,
+      '$lower_bound': ir_pb2.FunctionMapping.LOWER_BOUND,
   }
   return functions[text]
 
@@ -438,7 +438,7 @@
   # The function_name is a bit weird, but should suffice for any error messages
   # that might need it.
   return ir_pb2.Expression(
-      function=ir_pb2.Function(function=ir_pb2.Function.CHOICE,
+      function=ir_pb2.Function(function=ir_pb2.FunctionMapping.CHOICE,
                                args=[condition, if_true, if_false],
                                function_name=ir_pb2.Word(
                                    text='?:',
@@ -580,7 +580,7 @@
         e.source_location.start, comparison.source_location.end)
     e = ir_pb2.Expression(
         function=ir_pb2.Function(
-            function=ir_pb2.Function.AND,
+            function=ir_pb2.FunctionMapping.AND,
             args=[e, comparison],
             function_name=ir_pb2.Word(
                 text='&&',
@@ -840,7 +840,7 @@
 def _struct_body(indent, docs, attributes, types, fields, dedent):
   del indent, dedent  # Unused.
   return _structure_body(docs, attributes, types, fields,
-                         ir_pb2.TypeDefinition.BYTE)
+                         ir_pb2.AddressableUnit.BYTE)
 
 
 def _structure_body(docs, attributes, types, fields, addressable_unit):
@@ -941,7 +941,7 @@
 def _bits_body(indent, docs, attributes, types, fields, dedent):
   del indent, dedent  # Unused.
   return _structure_body(docs, attributes, types, fields,
-                         ir_pb2.TypeDefinition.BIT)
+                         ir_pb2.AddressableUnit.BIT)
 
 
 # Inline bits (defined as part of a field) are more restricted than standalone
@@ -951,7 +951,7 @@
 def _anonymous_bits_body(indent, attributes, fields, dedent):
   del indent, dedent  # Unused.
   return _structure_body(_List([]), attributes, _List([]), fields,
-                         ir_pb2.TypeDefinition.BIT)
+                         ir_pb2.AddressableUnit.BIT)
 
 
 # A field is:
@@ -1131,7 +1131,7 @@
       enumeration=ir_pb2.Enum(value=values.list),
       documentation=docs.list,
       attribute=attributes.list,
-      addressable_unit=ir_pb2.TypeDefinition.BIT)
+      addressable_unit=ir_pb2.AddressableUnit.BIT)
 
 
 # name = value
diff --git a/compiler/front_end/synthetics.py b/compiler/front_end/synthetics.py
index 7080527..8a6f856 100644
--- a/compiler/front_end/synthetics.py
+++ b/compiler/front_end/synthetics.py
@@ -156,8 +156,8 @@
 def _add_size_bound_virtuals(structure, type_definition):
   """Adds ${min,max}_size_in_{bits,bytes} virtual fields to structure."""
   names = {
-      ir_pb2.TypeDefinition.BIT: ("$max_size_in_bits", "$min_size_in_bits"),
-      ir_pb2.TypeDefinition.BYTE: ("$max_size_in_bytes", "$min_size_in_bytes"),
+      ir_pb2.AddressableUnit.BIT: ("$max_size_in_bits", "$min_size_in_bits"),
+      ir_pb2.AddressableUnit.BYTE: ("$max_size_in_bytes", "$min_size_in_bytes"),
   }
   for name in names[type_definition.addressable_unit]:
     bound_field = ir_pb2.Field(
@@ -184,8 +184,8 @@
 def _add_size_virtuals(structure, type_definition):
   """Adds a $size_in_bits or $size_in_bytes virtual field to structure."""
   names = {
-      ir_pb2.TypeDefinition.BIT: "$size_in_bits",
-      ir_pb2.TypeDefinition.BYTE: "$size_in_bytes",
+      ir_pb2.AddressableUnit.BIT: "$size_in_bits",
+      ir_pb2.AddressableUnit.BYTE: "$size_in_bytes",
   }
   size_field_name = names[type_definition.addressable_unit]
   size_clauses = []
diff --git a/compiler/front_end/synthetics_test.py b/compiler/front_end/synthetics_test.py
index ceafa11..bae2759 100644
--- a/compiler/front_end/synthetics_test.py
+++ b/compiler/front_end/synthetics_test.py
@@ -88,12 +88,12 @@
                      alias_field.existence_condition.function.args[1].function.
                      args[0].field_reference.path[1].source_name[-1].text)
     self.assertEqual(
-        ir_pb2.Function.PRESENCE,
+        ir_pb2.FunctionMapping.PRESENCE,
         alias_field.existence_condition.function.args[0].function.function)
     self.assertEqual(
-        ir_pb2.Function.PRESENCE,
+        ir_pb2.FunctionMapping.PRESENCE,
         alias_field.existence_condition.function.args[1].function.function)
-    self.assertEqual(ir_pb2.Function.AND,
+    self.assertEqual(ir_pb2.FunctionMapping.AND,
                      alias_field.existence_condition.function.function)
 
   def test_adds_correct_read_transform(self):
@@ -177,15 +177,15 @@
     max_size_in_bytes_field = structure.field[3]
     min_size_in_bytes_field = structure.field[4]
     self.assertEqual("$size_in_bytes", size_in_bytes_field.name.name.text)
-    self.assertEqual(ir_pb2.Function.MAXIMUM,
+    self.assertEqual(ir_pb2.FunctionMapping.MAXIMUM,
                      size_in_bytes_field.read_transform.function.function)
     self.assertEqual("$max_size_in_bytes",
                      max_size_in_bytes_field.name.name.text)
-    self.assertEqual(ir_pb2.Function.UPPER_BOUND,
+    self.assertEqual(ir_pb2.FunctionMapping.UPPER_BOUND,
                      max_size_in_bytes_field.read_transform.function.function)
     self.assertEqual("$min_size_in_bytes",
                      min_size_in_bytes_field.name.name.text)
-    self.assertEqual(ir_pb2.Function.LOWER_BOUND,
+    self.assertEqual(ir_pb2.FunctionMapping.LOWER_BOUND,
                      min_size_in_bytes_field.read_transform.function.function)
     # The correctness of $size_in_bytes et al are tested much further down
     # stream, in tests of the generated C++ code.
@@ -200,15 +200,15 @@
     max_size_in_bits_field = structure.field[3]
     min_size_in_bits_field = structure.field[4]
     self.assertEqual("$size_in_bits", size_in_bits_field.name.name.text)
-    self.assertEqual(ir_pb2.Function.MAXIMUM,
+    self.assertEqual(ir_pb2.FunctionMapping.MAXIMUM,
                      size_in_bits_field.read_transform.function.function)
     self.assertEqual("$max_size_in_bits",
                      max_size_in_bits_field.name.name.text)
-    self.assertEqual(ir_pb2.Function.UPPER_BOUND,
+    self.assertEqual(ir_pb2.FunctionMapping.UPPER_BOUND,
                      max_size_in_bits_field.read_transform.function.function)
     self.assertEqual("$min_size_in_bits",
                      min_size_in_bits_field.name.name.text)
-    self.assertEqual(ir_pb2.Function.LOWER_BOUND,
+    self.assertEqual(ir_pb2.FunctionMapping.LOWER_BOUND,
                      min_size_in_bits_field.read_transform.function.function)
     # The correctness of $size_in_bits et al are tested much further down
     # stream, in tests of the generated C++ code.
@@ -232,7 +232,7 @@
     self.assertEqual([], synthetics.desugar(ir))
     offset_of_b = ir.module[0].type[0].structure.field[1].location.start
     self.assertTrue(offset_of_b.HasField("function"))
-    self.assertEqual(offset_of_b.function.function, ir_pb2.Function.ADDITION)
+    self.assertEqual(offset_of_b.function.function, ir_pb2.FunctionMapping.ADDITION)
     self.assertEqual(offset_of_b.function.args[0].constant.value, "1")
     self.assertEqual(offset_of_b.function.args[1].constant.value, "2")
     offset_of_c = ir.module[0].type[0].structure.field[2].location.start
diff --git a/compiler/front_end/type_check.py b/compiler/front_end/type_check.py
index edc9d93..c15aaad 100644
--- a/compiler/front_end/type_check.py
+++ b/compiler/front_end/type_check.py
@@ -111,11 +111,11 @@
   for arg in expression.function.args:
     _type_check_expression(arg, source_file_name, ir, errors)
   function = expression.function.function
-  if function in (ir_pb2.Function.EQUALITY, ir_pb2.Function.INEQUALITY,
-                  ir_pb2.Function.LESS, ir_pb2.Function.LESS_OR_EQUAL,
-                  ir_pb2.Function.GREATER, ir_pb2.Function.GREATER_OR_EQUAL):
+  if function in (ir_pb2.FunctionMapping.EQUALITY, ir_pb2.FunctionMapping.INEQUALITY,
+                  ir_pb2.FunctionMapping.LESS, ir_pb2.FunctionMapping.LESS_OR_EQUAL,
+                  ir_pb2.FunctionMapping.GREATER, ir_pb2.FunctionMapping.GREATER_OR_EQUAL):
     _type_check_comparison_operator(expression, source_file_name, errors)
-  elif function == ir_pb2.Function.CHOICE:
+  elif function == ir_pb2.FunctionMapping.CHOICE:
     _type_check_choice_operator(expression, source_file_name, errors)
   else:
     _type_check_monomorphic_operator(expression, source_file_name, errors)
@@ -132,21 +132,21 @@
   binary = ("Left argument", "Right argument")
   n_ary = ("Argument {}".format(n) for n in range(len(args)))
   functions = {
-      ir_pb2.Function.ADDITION: (int_result, int_args, binary, 2, 2,
+      ir_pb2.FunctionMapping.ADDITION: (int_result, int_args, binary, 2, 2,
                                  "operator"),
-      ir_pb2.Function.SUBTRACTION: (int_result, int_args, binary, 2, 2,
+      ir_pb2.FunctionMapping.SUBTRACTION: (int_result, int_args, binary, 2, 2,
                                     "operator"),
-      ir_pb2.Function.MULTIPLICATION: (int_result, int_args, binary, 2, 2,
+      ir_pb2.FunctionMapping.MULTIPLICATION: (int_result, int_args, binary, 2, 2,
                                        "operator"),
-      ir_pb2.Function.AND: (bool_result, bool_args, binary, 2, 2, "operator"),
-      ir_pb2.Function.OR: (bool_result, bool_args, binary, 2, 2, "operator"),
-      ir_pb2.Function.MAXIMUM: (int_result, int_args, n_ary, 1, None,
+      ir_pb2.FunctionMapping.AND: (bool_result, bool_args, binary, 2, 2, "operator"),
+      ir_pb2.FunctionMapping.OR: (bool_result, bool_args, binary, 2, 2, "operator"),
+      ir_pb2.FunctionMapping.MAXIMUM: (int_result, int_args, n_ary, 1, None,
                                 "function"),
-      ir_pb2.Function.PRESENCE: (bool_result, field_args, n_ary, 1, 1,
+      ir_pb2.FunctionMapping.PRESENCE: (bool_result, field_args, n_ary, 1, 1,
                                  "function"),
-      ir_pb2.Function.UPPER_BOUND: (int_result, int_args, n_ary, 1, 1,
+      ir_pb2.FunctionMapping.UPPER_BOUND: (int_result, int_args, n_ary, 1, 1,
                                     "function"),
-      ir_pb2.Function.LOWER_BOUND: (int_result, int_args, n_ary, 1, 1,
+      ir_pb2.FunctionMapping.LOWER_BOUND: (int_result, int_args, n_ary, 1, 1,
                                     "function"),
   }
   function = expression.function.function
@@ -267,8 +267,8 @@
   """Checks the type of a comparison operator (==, !=, <, >, >=, <=)."""
   # Applying less than or greater than to a boolean is likely a mistake, so
   # only equality and inequality are allowed for booleans.
-  if expression.function.function in (ir_pb2.Function.EQUALITY,
-                                      ir_pb2.Function.INEQUALITY):
+  if expression.function.function in (ir_pb2.FunctionMapping.EQUALITY,
+                                      ir_pb2.FunctionMapping.INEQUALITY):
     acceptable_types = ("integer", "boolean", "enumeration")
     acceptable_types_for_humans = "an integer, boolean, or enum"
   else:
diff --git a/compiler/front_end/write_inference.py b/compiler/front_end/write_inference.py
index 09501f0..bb5b1f4 100644
--- a/compiler/front_end/write_inference.py
+++ b/compiler/front_end/write_inference.py
@@ -151,10 +151,10 @@
   # Note that any equation that can be solved here becomes part of Emboss's
   # contract, forever, so be conservative in expanding its solving capabilities!
   for index in reference_path:
-    if subexpression.function.function == ir_pb2.Function.ADDITION:
+    if subexpression.function.function == ir_pb2.FunctionMapping.ADDITION:
       result = ir_pb2.Expression(
           function=ir_pb2.Function(
-              function=ir_pb2.Function.SUBTRACTION,
+              function=ir_pb2.FunctionMapping.SUBTRACTION,
               args=[
                   result,
                   subexpression.function.args[1 - index],
@@ -162,11 +162,11 @@
           ),
           type=ir_pb2.ExpressionType(integer=ir_pb2.IntegerType())
       )
-    elif subexpression.function.function == ir_pb2.Function.SUBTRACTION:
+    elif subexpression.function.function == ir_pb2.FunctionMapping.SUBTRACTION:
       if index == 0:
         result = ir_pb2.Expression(
             function=ir_pb2.Function(
-                function=ir_pb2.Function.ADDITION,
+                function=ir_pb2.FunctionMapping.ADDITION,
                 args=[
                     result,
                     subexpression.function.args[1],
@@ -177,7 +177,7 @@
       else:
         result = ir_pb2.Expression(
             function=ir_pb2.Function(
-                function=ir_pb2.Function.SUBTRACTION,
+                function=ir_pb2.FunctionMapping.SUBTRACTION,
                 args=[
                     subexpression.function.args[0],
                     result,
diff --git a/compiler/front_end/write_inference_test.py b/compiler/front_end/write_inference_test.py
index 52e8c26..9915196 100644
--- a/compiler/front_end/write_inference_test.py
+++ b/compiler/front_end/write_inference_test.py
@@ -101,7 +101,7 @@
     self.assertEqual(
         "x",
         transform.destination.path[0].canonical_name.object_path[-1])
-    self.assertEqual(ir_pb2.Function.SUBTRACTION,
+    self.assertEqual(ir_pb2.FunctionMapping.SUBTRACTION,
                      transform.function_body.function.function)
     arg0, arg1 = transform.function_body.function.args
     self.assertEqual("$logical_value",
@@ -119,7 +119,7 @@
     self.assertEqual(
         "x",
         transform.destination.path[0].canonical_name.object_path[-1])
-    self.assertEqual(ir_pb2.Function.ADDITION,
+    self.assertEqual(ir_pb2.FunctionMapping.ADDITION,
                      transform.function_body.function.function)
     arg0, arg1 = transform.function_body.function.args
     self.assertEqual("$logical_value",
@@ -137,7 +137,7 @@
     self.assertEqual(
         "x",
         transform.destination.path[0].canonical_name.object_path[-1])
-    self.assertEqual(ir_pb2.Function.SUBTRACTION,
+    self.assertEqual(ir_pb2.FunctionMapping.SUBTRACTION,
                      transform.function_body.function.function)
     arg0, arg1 = transform.function_body.function.args
     self.assertEqual("$logical_value",
@@ -156,7 +156,7 @@
     self.assertEqual(
         "x",
         transform.destination.path[0].canonical_name.object_path[-1])
-    self.assertEqual(ir_pb2.Function.SUBTRACTION,
+    self.assertEqual(ir_pb2.FunctionMapping.SUBTRACTION,
                      transform.function_body.function.function)
     arg0, arg1 = transform.function_body.function.args
     self.assertEqual("50", arg0.constant.value)
@@ -174,11 +174,11 @@
     self.assertEqual(
         "x",
         transform.destination.path[0].canonical_name.object_path[-1])
-    self.assertEqual(ir_pb2.Function.SUBTRACTION,
+    self.assertEqual(ir_pb2.FunctionMapping.SUBTRACTION,
                      transform.function_body.function.function)
     arg0, arg1 = transform.function_body.function.args
     self.assertEqual("50", arg0.constant.value)
-    self.assertEqual(ir_pb2.Function.SUBTRACTION, arg1.function.function)
+    self.assertEqual(ir_pb2.FunctionMapping.SUBTRACTION, arg1.function.function)
     arg10, arg11 = arg1.function.args
     self.assertEqual("$logical_value",
                      arg10.builtin_reference.canonical_name.object_path[0])
@@ -204,7 +204,7 @@
     self.assertEqual(
         "x",
         transform.destination.path[0].canonical_name.object_path[-1])
-    self.assertEqual(ir_pb2.Function.ADDITION,
+    self.assertEqual(ir_pb2.FunctionMapping.ADDITION,
                      transform.function_body.function.function)
     args = transform.function_body.function.args
     self.assertEqual("$logical_value",
diff --git a/compiler/util/attribute_util.py b/compiler/util/attribute_util.py
index d864364..8c4e2b7 100644
--- a/compiler/util/attribute_util.py
+++ b/compiler/util/attribute_util.py
@@ -167,12 +167,12 @@
 
   def check_type_definition(type_definition, source_file_name, errors):
     if type_definition.HasField("structure"):
-      if type_definition.addressable_unit == ir_pb2.TypeDefinition.BYTE:
+      if type_definition.addressable_unit == ir_pb2.AddressableUnit.BYTE:
         errors.extend(_check_attributes(
             type_definition.attribute, types, back_end, struct_attributes,
             "struct '{}'".format(
                 type_definition.name.name.text), source_file_name))
-      elif type_definition.addressable_unit == ir_pb2.TypeDefinition.BIT:
+      elif type_definition.addressable_unit == ir_pb2.AddressableUnit.BIT:
         errors.extend(_check_attributes(
             type_definition.attribute, types, back_end, bits_attributes,
             "bits '{}'".format(
diff --git a/compiler/util/ir_pb2.py b/compiler/util/ir_pb2.py
index d1b5dd3..6ee0b47 100644
--- a/compiler/util/ir_pb2.py
+++ b/compiler/util/ir_pb2.py
@@ -29,6 +29,7 @@
 everything that touches the IR (i.e., the entire compiler).
 """
 
+import enum
 import json
 import sys
 
@@ -107,9 +108,12 @@
       self._set_value(obj, self._type(**value))
     elif isinstance(value, _Text) and self._decode_names:
       self._set_value(obj, self._type(self._decode_names(value)))
+    elif isinstance(value, _Text) and issubclass(self._type, enum.Enum):
+      self._set_value(obj, getattr(self._type, value))
     elif (not isinstance(value, self._type) and
           not (self._type == _Int and isinstance(value, _int_types)) and
-          not (self._type == _Text and isinstance(value, _text_types))):
+          not (self._type == _Text and isinstance(value, _text_types)) and
+          not (issubclass(self._type, enum.Enum) and isinstance(value, _int_types))):
       raise AttributeError("Cannot set {} (type {}) for type {}".format(
           value, value.__class__, self._type))
     elif issubclass(self._type, Message):
@@ -427,9 +431,8 @@
   source_location = Optional(Location)
 
 
-@message
-class Function(Message):
-  """IR for a single function (+, -, *, ==, $max, etc.) in an expression."""
+class FunctionMapping(int, enum.Enum):
+  """Enum of supported function types"""
   UNKNOWN = 0
   ADDITION = 1           # +
   SUBTRACTION = 2        # -
@@ -448,8 +451,13 @@
   UPPER_BOUND = 15       # $upper_bound()
   LOWER_BOUND = 16       # $lower_bound()
 
+
+@message
+class Function(Message):
+  """IR for a single function (+, -, *, ==, $max, etc.) in an expression."""
+
   # pylint:disable=undefined-variable
-  function = Optional(int, decode_names=lambda x: getattr(Function, x))
+  function = Optional(FunctionMapping)
   args = Repeated(lambda: Expression)
   function_name = Optional(Word)
   source_location = Optional(Location)
@@ -952,19 +960,22 @@
   source_location = Optional(Location)
 
 
-@message
-class TypeDefinition(Message):
-  """Container IR for a type definition (struct, union, etc.)"""
-
-  # The "addressable unit" is the size of the smallest unit that can be read
-  # from the backing store that this type expects.  For `struct`s, this is
-  # BYTE; for `enum`s and `bits`, this is BIT, and for `external`s it depends
-  # on the specific type.
+class AddressableUnit(int, enum.Enum):
+  """The "addressable unit" is the size of the smallest unit that can be read
+  from the backing store that this type expects.  For `struct`s, this is
+  BYTE; for `enum`s and `bits`, this is BIT, and for `external`s it depends
+  on the specific type
+  """
 
   NONE = 0
   BIT = 1
   BYTE = 8
 
+
+@message
+class TypeDefinition(Message):
+  """Container IR for a type definition (struct, union, etc.)"""
+
   external = Optional(External, "type")
   enumeration = Optional(Enum, "type")
   structure = Optional(Structure, "type")
@@ -974,8 +985,7 @@
   documentation = Repeated(Documentation)     # Docs for the type.
   # pylint:disable=undefined-variable
   subtype = Repeated(lambda: TypeDefinition)  # Subtypes of this type.
-  addressable_unit = Optional(
-      int, decode_names=lambda x: getattr(TypeDefinition, x))
+  addressable_unit = Optional(AddressableUnit)
 
   # If the type requires parameters at runtime, these are its parameters.
   # These are currently only allowed on structures, but in the future they
diff --git a/compiler/util/ir_util.py b/compiler/util/ir_util.py
index 4000e70..15a2def 100644
--- a/compiler/util/ir_util.py
+++ b/compiler/util/ir_util.py
@@ -150,21 +150,23 @@
   # constant expression patterns built from non-constant subexpressions, such as
   # `0 * X` or `X == X` or `3 * X == X + X + X`.  I (bolms@) am not implementing
   # any further special cases because I do not see any practical use for them.
-  if function.function == ir_pb2.Function.AND:
+  if function.function == ir_pb2.FunctionMapping.UNKNOWN:
+    return None
+  if function.function == ir_pb2.FunctionMapping.AND:
     if any(value is False for value in values):
       return False
     elif any(value is None for value in values):
       return None
     else:
       return True
-  elif function.function == ir_pb2.Function.OR:
+  elif function.function == ir_pb2.FunctionMapping.OR:
     if any(value is True for value in values):
       return True
     elif any(value is None for value in values):
       return None
     else:
       return False
-  elif function.function == ir_pb2.Function.CHOICE:
+  elif function.function == ir_pb2.FunctionMapping.CHOICE:
     if values[0] is None:
       return None
     else:
@@ -174,18 +176,18 @@
   if any(value is None for value in values):
     return None
   functions = {
-      ir_pb2.Function.ADDITION: operator.add,
-      ir_pb2.Function.SUBTRACTION: operator.sub,
-      ir_pb2.Function.MULTIPLICATION: operator.mul,
-      ir_pb2.Function.EQUALITY: operator.eq,
-      ir_pb2.Function.INEQUALITY: operator.ne,
-      ir_pb2.Function.LESS: operator.lt,
-      ir_pb2.Function.LESS_OR_EQUAL: operator.le,
-      ir_pb2.Function.GREATER: operator.gt,
-      ir_pb2.Function.GREATER_OR_EQUAL: operator.ge,
+      ir_pb2.FunctionMapping.ADDITION: operator.add,
+      ir_pb2.FunctionMapping.SUBTRACTION: operator.sub,
+      ir_pb2.FunctionMapping.MULTIPLICATION: operator.mul,
+      ir_pb2.FunctionMapping.EQUALITY: operator.eq,
+      ir_pb2.FunctionMapping.INEQUALITY: operator.ne,
+      ir_pb2.FunctionMapping.LESS: operator.lt,
+      ir_pb2.FunctionMapping.LESS_OR_EQUAL: operator.le,
+      ir_pb2.FunctionMapping.GREATER: operator.gt,
+      ir_pb2.FunctionMapping.GREATER_OR_EQUAL: operator.ge,
       # Python's max([1, 2]) == 2; max(1, 2) == 2; max([1]) == 1; but max(1)
       # throws a TypeError ("'int' object is not iterable").
-      ir_pb2.Function.MAXIMUM: lambda *x: max(x),
+      ir_pb2.FunctionMapping.MAXIMUM: lambda *x: max(x),
   }
   return functions[function.function](*values)
 
diff --git a/compiler/util/ir_util_test.py b/compiler/util/ir_util_test.py
index 44a8063..7743c10 100644
--- a/compiler/util/ir_util_test.py
+++ b/compiler/util/ir_util_test.py
@@ -351,7 +351,7 @@
             value=ir_pb2.AttributeValue(
                 expression=ir_pb2.Expression(
                     function=ir_pb2.Function(
-                        function=ir_pb2.Function.ADDITION,
+                        function=ir_pb2.FunctionMapping.ADDITION,
                         args=[
                             ir_pb2.Expression(
                                 constant=ir_pb2.NumericConstant(value="100"),