xref: /aosp_15_r20/external/emboss/compiler/front_end/constraints.py (revision 99e0aae7469b87d12f0ad23e61142c2d74c1ef70)
1*99e0aae7SDavid Rees# Copyright 2019 Google LLC
2*99e0aae7SDavid Rees#
3*99e0aae7SDavid Rees# Licensed under the Apache License, Version 2.0 (the "License");
4*99e0aae7SDavid Rees# you may not use this file except in compliance with the License.
5*99e0aae7SDavid Rees# You may obtain a copy of the License at
6*99e0aae7SDavid Rees#
7*99e0aae7SDavid Rees#     https://www.apache.org/licenses/LICENSE-2.0
8*99e0aae7SDavid Rees#
9*99e0aae7SDavid Rees# Unless required by applicable law or agreed to in writing, software
10*99e0aae7SDavid Rees# distributed under the License is distributed on an "AS IS" BASIS,
11*99e0aae7SDavid Rees# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*99e0aae7SDavid Rees# See the License for the specific language governing permissions and
13*99e0aae7SDavid Rees# limitations under the License.
14*99e0aae7SDavid Rees
15*99e0aae7SDavid Rees"""Routines to check miscellaneous constraints on the IR."""
16*99e0aae7SDavid Rees
17*99e0aae7SDavid Reesfrom compiler.front_end import attributes
18*99e0aae7SDavid Reesfrom compiler.util import error
19*99e0aae7SDavid Reesfrom compiler.util import ir_data
20*99e0aae7SDavid Reesfrom compiler.util import ir_data_utils
21*99e0aae7SDavid Reesfrom compiler.util import ir_util
22*99e0aae7SDavid Reesfrom compiler.util import resources
23*99e0aae7SDavid Reesfrom compiler.util import traverse_ir
24*99e0aae7SDavid Rees
25*99e0aae7SDavid Rees
26*99e0aae7SDavid Reesdef _render_type(type_ir, ir):
27*99e0aae7SDavid Rees  """Returns the human-readable notation of the given type."""
28*99e0aae7SDavid Rees  assert type_ir.HasField("atomic_type"), (
29*99e0aae7SDavid Rees      "TODO(bolms): Implement _render_type for array types.")
30*99e0aae7SDavid Rees  if type_ir.HasField("size_in_bits"):
31*99e0aae7SDavid Rees    return _render_atomic_type_name(
32*99e0aae7SDavid Rees        type_ir,
33*99e0aae7SDavid Rees        ir,
34*99e0aae7SDavid Rees        suffix=":" + str(ir_util.constant_value(type_ir.size_in_bits)))
35*99e0aae7SDavid Rees  else:
36*99e0aae7SDavid Rees    return _render_atomic_type_name(type_ir, ir)
37*99e0aae7SDavid Rees
38*99e0aae7SDavid Rees
39*99e0aae7SDavid Reesdef _render_atomic_type_name(type_ir, ir, suffix=None):
40*99e0aae7SDavid Rees  assert type_ir.HasField("atomic_type"), (
41*99e0aae7SDavid Rees      "_render_atomic_type_name() requires an atomic type")
42*99e0aae7SDavid Rees  if not suffix:
43*99e0aae7SDavid Rees    suffix = ""
44*99e0aae7SDavid Rees  type_definition = ir_util.find_object(type_ir.atomic_type.reference, ir)
45*99e0aae7SDavid Rees  if type_definition.name.is_anonymous:
46*99e0aae7SDavid Rees    return "anonymous type"
47*99e0aae7SDavid Rees  else:
48*99e0aae7SDavid Rees    return "type '{}{}'".format(type_definition.name.name.text, suffix)
49*99e0aae7SDavid Rees
50*99e0aae7SDavid Rees
51*99e0aae7SDavid Reesdef _check_that_inner_array_dimensions_are_constant(
52*99e0aae7SDavid Rees    type_ir, source_file_name, errors):
53*99e0aae7SDavid Rees  """Checks that inner array dimensions are constant."""
54*99e0aae7SDavid Rees  if type_ir.WhichOneof("size") == "automatic":
55*99e0aae7SDavid Rees    errors.append([error.error(
56*99e0aae7SDavid Rees        source_file_name,
57*99e0aae7SDavid Rees        ir_data_utils.reader(type_ir).element_count.source_location,
58*99e0aae7SDavid Rees        "Array dimensions can only be omitted for the outermost dimension.")])
59*99e0aae7SDavid Rees  elif type_ir.WhichOneof("size") == "element_count":
60*99e0aae7SDavid Rees    if not ir_util.is_constant(type_ir.element_count):
61*99e0aae7SDavid Rees      errors.append([error.error(source_file_name,
62*99e0aae7SDavid Rees                                 type_ir.element_count.source_location,
63*99e0aae7SDavid Rees                                 "Inner array dimensions must be constant.")])
64*99e0aae7SDavid Rees  else:
65*99e0aae7SDavid Rees    assert False, 'Expected "element_count" or "automatic" array size.'
66*99e0aae7SDavid Rees
67*99e0aae7SDavid Rees
68*99e0aae7SDavid Reesdef _check_that_array_base_types_are_fixed_size(type_ir, source_file_name,
69*99e0aae7SDavid Rees                                                errors, ir):
70*99e0aae7SDavid Rees  """Checks that the sizes of array elements are known at compile time."""
71*99e0aae7SDavid Rees  if type_ir.base_type.HasField("array_type"):
72*99e0aae7SDavid Rees    # An array is fixed size if its base_type is fixed size and its array
73*99e0aae7SDavid Rees    # dimension is constant.  This function will be called again on the inner
74*99e0aae7SDavid Rees    # array, and we do not want to cascade errors if the inner array's base_type
75*99e0aae7SDavid Rees    # is not fixed size.  The array dimensions are separately checked by
76*99e0aae7SDavid Rees    # _check_that_inner_array_dimensions_are_constant, which will provide an
77*99e0aae7SDavid Rees    # appropriate error message for that case.
78*99e0aae7SDavid Rees    return
79*99e0aae7SDavid Rees  assert type_ir.base_type.HasField("atomic_type")
80*99e0aae7SDavid Rees  if type_ir.base_type.HasField("size_in_bits"):
81*99e0aae7SDavid Rees    # If the base_type has a size_in_bits, then it is fixed size.
82*99e0aae7SDavid Rees    return
83*99e0aae7SDavid Rees  base_type = ir_util.find_object(type_ir.base_type.atomic_type.reference, ir)
84*99e0aae7SDavid Rees  base_type_fixed_size = ir_util.get_integer_attribute(
85*99e0aae7SDavid Rees      base_type.attribute, attributes.FIXED_SIZE)
86*99e0aae7SDavid Rees  if base_type_fixed_size is None:
87*99e0aae7SDavid Rees    errors.append([error.error(source_file_name,
88*99e0aae7SDavid Rees                               type_ir.base_type.atomic_type.source_location,
89*99e0aae7SDavid Rees                               "Array elements must be fixed size.")])
90*99e0aae7SDavid Rees
91*99e0aae7SDavid Rees
92*99e0aae7SDavid Reesdef _check_that_array_base_types_in_structs_are_multiples_of_bytes(
93*99e0aae7SDavid Rees    type_ir, type_definition, source_file_name, errors, ir):
94*99e0aae7SDavid Rees  # TODO(bolms): Remove this limitation.
95*99e0aae7SDavid Rees  """Checks that the sizes of array elements are multiples of 8 bits."""
96*99e0aae7SDavid Rees  if type_ir.base_type.HasField("array_type"):
97*99e0aae7SDavid Rees    # Only check the innermost array for multidimensional arrays.
98*99e0aae7SDavid Rees    return
99*99e0aae7SDavid Rees  assert type_ir.base_type.HasField("atomic_type")
100*99e0aae7SDavid Rees  if type_ir.base_type.HasField("size_in_bits"):
101*99e0aae7SDavid Rees    assert ir_util.is_constant(type_ir.base_type.size_in_bits)
102*99e0aae7SDavid Rees    base_type_size = ir_util.constant_value(type_ir.base_type.size_in_bits)
103*99e0aae7SDavid Rees  else:
104*99e0aae7SDavid Rees    fixed_size = ir_util.fixed_size_of_type_in_bits(type_ir.base_type, ir)
105*99e0aae7SDavid Rees    if fixed_size is None:
106*99e0aae7SDavid Rees      # Variable-sized elements are checked elsewhere.
107*99e0aae7SDavid Rees      return
108*99e0aae7SDavid Rees    base_type_size = fixed_size
109*99e0aae7SDavid Rees  if base_type_size % type_definition.addressable_unit != 0:
110*99e0aae7SDavid Rees    assert type_definition.addressable_unit == ir_data.AddressableUnit.BYTE
111*99e0aae7SDavid Rees    errors.append([error.error(source_file_name,
112*99e0aae7SDavid Rees                               type_ir.base_type.source_location,
113*99e0aae7SDavid Rees                               "Array elements in structs must have sizes "
114*99e0aae7SDavid Rees                               "which are a multiple of 8 bits.")])
115*99e0aae7SDavid Rees
116*99e0aae7SDavid Rees
117*99e0aae7SDavid Reesdef _check_constancy_of_constant_references(expression, source_file_name,
118*99e0aae7SDavid Rees                                            errors, ir):
119*99e0aae7SDavid Rees  """Checks that constant_references are constant."""
120*99e0aae7SDavid Rees  if expression.WhichOneof("expression") != "constant_reference":
121*99e0aae7SDavid Rees    return
122*99e0aae7SDavid Rees  # This is a bit of a hack: really, we want to know that the referred-to object
123*99e0aae7SDavid Rees  # has no dependencies on any instance variables of its parent structure; i.e.,
124*99e0aae7SDavid Rees  # that its value does not depend on having a view of the structure.
125*99e0aae7SDavid Rees  if not ir_util.is_constant_type(expression.type):
126*99e0aae7SDavid Rees    referred_name = expression.constant_reference.canonical_name
127*99e0aae7SDavid Rees    referred_object = ir_util.find_object(referred_name, ir)
128*99e0aae7SDavid Rees    errors.append([
129*99e0aae7SDavid Rees        error.error(
130*99e0aae7SDavid Rees            source_file_name, expression.source_location,
131*99e0aae7SDavid Rees            "Static references must refer to constants."),
132*99e0aae7SDavid Rees        error.note(
133*99e0aae7SDavid Rees            referred_name.module_file, referred_object.source_location,
134*99e0aae7SDavid Rees            "{} is not constant.".format(referred_name.object_path[-1]))
135*99e0aae7SDavid Rees    ])
136*99e0aae7SDavid Rees
137*99e0aae7SDavid Rees
138*99e0aae7SDavid Reesdef _check_that_enum_values_are_representable(enum_type, type_definition,
139*99e0aae7SDavid Rees                                              source_file_name, errors):
140*99e0aae7SDavid Rees  """Checks that enumeration values can fit in their specified int type."""
141*99e0aae7SDavid Rees  values = []
142*99e0aae7SDavid Rees  max_enum_size = ir_util.get_integer_attribute(
143*99e0aae7SDavid Rees      type_definition.attribute, attributes.ENUM_MAXIMUM_BITS)
144*99e0aae7SDavid Rees  is_signed = ir_util.get_boolean_attribute(
145*99e0aae7SDavid Rees      type_definition.attribute, attributes.IS_SIGNED)
146*99e0aae7SDavid Rees  if is_signed:
147*99e0aae7SDavid Rees    enum_range = (-(2**(max_enum_size-1)), 2**(max_enum_size-1)-1)
148*99e0aae7SDavid Rees  else:
149*99e0aae7SDavid Rees    enum_range = (0, 2**max_enum_size-1)
150*99e0aae7SDavid Rees  for value in enum_type.value:
151*99e0aae7SDavid Rees    values.append((ir_util.constant_value(value.value), value))
152*99e0aae7SDavid Rees  out_of_range = [v for v in values
153*99e0aae7SDavid Rees                  if not enum_range[0] <= v[0] <= enum_range[1]]
154*99e0aae7SDavid Rees  # If all values are in range, this loop will have zero iterations.
155*99e0aae7SDavid Rees  for value in out_of_range:
156*99e0aae7SDavid Rees    errors.append([
157*99e0aae7SDavid Rees        error.error(
158*99e0aae7SDavid Rees            source_file_name, value[1].value.source_location,
159*99e0aae7SDavid Rees            "Value {} is out of range for {}-bit {} enumeration.".format(
160*99e0aae7SDavid Rees                value[0], max_enum_size, "signed" if is_signed else "unsigned"))
161*99e0aae7SDavid Rees    ])
162*99e0aae7SDavid Rees
163*99e0aae7SDavid Rees
164*99e0aae7SDavid Reesdef _field_size(field, type_definition):
165*99e0aae7SDavid Rees  """Calculates the size of the given field in bits, if it is constant."""
166*99e0aae7SDavid Rees  size = ir_util.constant_value(field.location.size)
167*99e0aae7SDavid Rees  if size is None:
168*99e0aae7SDavid Rees    return None
169*99e0aae7SDavid Rees  return size * type_definition.addressable_unit
170*99e0aae7SDavid Rees
171*99e0aae7SDavid Rees
172*99e0aae7SDavid Reesdef _check_type_requirements_for_field(type_ir, type_definition, field, ir,
173*99e0aae7SDavid Rees                                       source_file_name, errors):
174*99e0aae7SDavid Rees  """Checks that the `requires` attribute of each field's type is fulfilled."""
175*99e0aae7SDavid Rees  if not type_ir.HasField("atomic_type"):
176*99e0aae7SDavid Rees    return
177*99e0aae7SDavid Rees
178*99e0aae7SDavid Rees  if field.type.HasField("atomic_type"):
179*99e0aae7SDavid Rees    field_min_size = (int(field.location.size.type.integer.minimum_value) *
180*99e0aae7SDavid Rees                      type_definition.addressable_unit)
181*99e0aae7SDavid Rees    field_max_size = (int(field.location.size.type.integer.maximum_value) *
182*99e0aae7SDavid Rees                      type_definition.addressable_unit)
183*99e0aae7SDavid Rees    field_is_atomic = True
184*99e0aae7SDavid Rees  else:
185*99e0aae7SDavid Rees    field_is_atomic = False
186*99e0aae7SDavid Rees
187*99e0aae7SDavid Rees  if type_ir.HasField("size_in_bits"):
188*99e0aae7SDavid Rees    element_size = ir_util.constant_value(type_ir.size_in_bits)
189*99e0aae7SDavid Rees  else:
190*99e0aae7SDavid Rees    element_size = None
191*99e0aae7SDavid Rees
192*99e0aae7SDavid Rees  referenced_type_definition = ir_util.find_object(
193*99e0aae7SDavid Rees      type_ir.atomic_type.reference, ir)
194*99e0aae7SDavid Rees  type_is_anonymous = referenced_type_definition.name.is_anonymous
195*99e0aae7SDavid Rees  type_size_attr = ir_util.get_attribute(
196*99e0aae7SDavid Rees      referenced_type_definition.attribute, attributes.FIXED_SIZE)
197*99e0aae7SDavid Rees  if type_size_attr:
198*99e0aae7SDavid Rees    type_size = ir_util.constant_value(type_size_attr.expression)
199*99e0aae7SDavid Rees  else:
200*99e0aae7SDavid Rees    type_size = None
201*99e0aae7SDavid Rees
202*99e0aae7SDavid Rees  if (element_size is not None and type_size is not None and
203*99e0aae7SDavid Rees      element_size != type_size):
204*99e0aae7SDavid Rees    errors.append([
205*99e0aae7SDavid Rees        error.error(
206*99e0aae7SDavid Rees            source_file_name, type_ir.size_in_bits.source_location,
207*99e0aae7SDavid Rees            "Explicit size of {} bits does not match fixed size ({} bits) of "
208*99e0aae7SDavid Rees            "{}.".format(element_size, type_size,
209*99e0aae7SDavid Rees                         _render_atomic_type_name(type_ir, ir))),
210*99e0aae7SDavid Rees        error.note(
211*99e0aae7SDavid Rees            type_ir.atomic_type.reference.canonical_name.module_file,
212*99e0aae7SDavid Rees            type_size_attr.source_location,
213*99e0aae7SDavid Rees            "Size specified here.")
214*99e0aae7SDavid Rees    ])
215*99e0aae7SDavid Rees    return
216*99e0aae7SDavid Rees
217*99e0aae7SDavid Rees  # If the type had no size specifier (the ':32' in 'UInt:32'), but the type is
218*99e0aae7SDavid Rees  # fixed size, then continue as if the type's size were explicitly stated.
219*99e0aae7SDavid Rees  if element_size is None:
220*99e0aae7SDavid Rees    element_size = type_size
221*99e0aae7SDavid Rees
222*99e0aae7SDavid Rees  # TODO(bolms): When the full dynamic size expression for types is generated,
223*99e0aae7SDavid Rees  # add a check that dynamically-sized types can, at least potentially, fit in
224*99e0aae7SDavid Rees  # their fields.
225*99e0aae7SDavid Rees
226*99e0aae7SDavid Rees  if field_is_atomic and element_size is not None:
227*99e0aae7SDavid Rees    # If the field has a fixed size, and the (atomic) type contained therein is
228*99e0aae7SDavid Rees    # also fixed size, then the sizes should match.
229*99e0aae7SDavid Rees    #
230*99e0aae7SDavid Rees    # TODO(bolms): Maybe change the case where the field is bigger than
231*99e0aae7SDavid Rees    # necessary into a warning?
232*99e0aae7SDavid Rees    if (field_max_size == field_min_size and
233*99e0aae7SDavid Rees        (element_size > field_max_size or
234*99e0aae7SDavid Rees         (element_size < field_min_size and not type_is_anonymous))):
235*99e0aae7SDavid Rees      errors.append([
236*99e0aae7SDavid Rees          error.error(
237*99e0aae7SDavid Rees              source_file_name, type_ir.source_location,
238*99e0aae7SDavid Rees              "Fixed-size {} cannot be placed in field of size {} bits; "
239*99e0aae7SDavid Rees              "requires {} bits.".format(
240*99e0aae7SDavid Rees                  _render_type(type_ir, ir), field_max_size, element_size))
241*99e0aae7SDavid Rees      ])
242*99e0aae7SDavid Rees      return
243*99e0aae7SDavid Rees    elif element_size > field_max_size:
244*99e0aae7SDavid Rees      errors.append([
245*99e0aae7SDavid Rees          error.error(
246*99e0aae7SDavid Rees              source_file_name, type_ir.source_location,
247*99e0aae7SDavid Rees              "Field of maximum size {} bits cannot hold fixed-size {}, which "
248*99e0aae7SDavid Rees              "requires {} bits.".format(
249*99e0aae7SDavid Rees                  field_max_size, _render_type(type_ir, ir), element_size))
250*99e0aae7SDavid Rees      ])
251*99e0aae7SDavid Rees      return
252*99e0aae7SDavid Rees
253*99e0aae7SDavid Rees  # If we're here, then field/type sizes are consistent.
254*99e0aae7SDavid Rees  if (element_size is None and field_is_atomic and
255*99e0aae7SDavid Rees      field_min_size == field_max_size):
256*99e0aae7SDavid Rees    # From here down, we just use element_size.
257*99e0aae7SDavid Rees    element_size = field_min_size
258*99e0aae7SDavid Rees
259*99e0aae7SDavid Rees  errors.extend(_check_physical_type_requirements(
260*99e0aae7SDavid Rees      type_ir, field.source_location, element_size, ir, source_file_name))
261*99e0aae7SDavid Rees
262*99e0aae7SDavid Rees
263*99e0aae7SDavid Reesdef _check_type_requirements_for_parameter_type(
264*99e0aae7SDavid Rees    runtime_parameter, ir, source_file_name, errors):
265*99e0aae7SDavid Rees  """Checks that the type of a parameter is valid."""
266*99e0aae7SDavid Rees  physical_type = runtime_parameter.physical_type_alias
267*99e0aae7SDavid Rees  logical_type = runtime_parameter.type
268*99e0aae7SDavid Rees  size = ir_util.constant_value(physical_type.size_in_bits)
269*99e0aae7SDavid Rees  if logical_type.WhichOneof("type") == "integer":
270*99e0aae7SDavid Rees    integer_errors = _integer_bounds_errors(
271*99e0aae7SDavid Rees        logical_type.integer, "parameter", source_file_name,
272*99e0aae7SDavid Rees        physical_type.source_location)
273*99e0aae7SDavid Rees    if integer_errors:
274*99e0aae7SDavid Rees      errors.extend(integer_errors)
275*99e0aae7SDavid Rees      return
276*99e0aae7SDavid Rees    errors.extend(_check_physical_type_requirements(
277*99e0aae7SDavid Rees        physical_type, runtime_parameter.source_location,
278*99e0aae7SDavid Rees        size, ir, source_file_name))
279*99e0aae7SDavid Rees  elif logical_type.WhichOneof("type") == "enumeration":
280*99e0aae7SDavid Rees    if physical_type.HasField("size_in_bits"):
281*99e0aae7SDavid Rees      # This seems a little weird: for `UInt`, `Int`, etc., the explicit size is
282*99e0aae7SDavid Rees      # required, but for enums it is banned.  This is because enums have a
283*99e0aae7SDavid Rees      # "native" 64-bit size in expressions, so the physical size is just
284*99e0aae7SDavid Rees      # ignored.
285*99e0aae7SDavid Rees      errors.extend([[
286*99e0aae7SDavid Rees          error.error(
287*99e0aae7SDavid Rees              source_file_name, physical_type.size_in_bits.source_location,
288*99e0aae7SDavid Rees              "Parameters with enum type may not have explicit size.")
289*99e0aae7SDavid Rees
290*99e0aae7SDavid Rees      ]])
291*99e0aae7SDavid Rees  else:
292*99e0aae7SDavid Rees    assert False, "Non-integer/enum parameters should have been caught earlier."
293*99e0aae7SDavid Rees
294*99e0aae7SDavid Rees
295*99e0aae7SDavid Reesdef _check_physical_type_requirements(
296*99e0aae7SDavid Rees    type_ir, usage_source_location, size, ir, source_file_name):
297*99e0aae7SDavid Rees  """Checks that the given atomic `type_ir` is allowed to be `size` bits."""
298*99e0aae7SDavid Rees  referenced_type_definition = ir_util.find_object(
299*99e0aae7SDavid Rees      type_ir.atomic_type.reference, ir)
300*99e0aae7SDavid Rees  if referenced_type_definition.HasField("enumeration"):
301*99e0aae7SDavid Rees    if size is None:
302*99e0aae7SDavid Rees      return [[
303*99e0aae7SDavid Rees          error.error(
304*99e0aae7SDavid Rees              source_file_name, type_ir.source_location,
305*99e0aae7SDavid Rees              "Enumeration {} cannot be placed in a dynamically-sized "
306*99e0aae7SDavid Rees              "field.".format(_render_type(type_ir, ir)))
307*99e0aae7SDavid Rees      ]]
308*99e0aae7SDavid Rees    else:
309*99e0aae7SDavid Rees      max_enum_size = ir_util.get_integer_attribute(
310*99e0aae7SDavid Rees          referenced_type_definition.attribute, attributes.ENUM_MAXIMUM_BITS)
311*99e0aae7SDavid Rees      if size < 1 or size > max_enum_size:
312*99e0aae7SDavid Rees        return [[
313*99e0aae7SDavid Rees            error.error(
314*99e0aae7SDavid Rees                source_file_name, type_ir.source_location,
315*99e0aae7SDavid Rees                "Enumeration {} cannot be {} bits; {} must be between "
316*99e0aae7SDavid Rees                "1 and {} bits, inclusive.".format(
317*99e0aae7SDavid Rees                    _render_atomic_type_name(type_ir, ir), size,
318*99e0aae7SDavid Rees                    _render_atomic_type_name(type_ir, ir), max_enum_size))
319*99e0aae7SDavid Rees        ]]
320*99e0aae7SDavid Rees
321*99e0aae7SDavid Rees  if size is None:
322*99e0aae7SDavid Rees    bindings = {"$is_statically_sized": False}
323*99e0aae7SDavid Rees  else:
324*99e0aae7SDavid Rees    bindings = {
325*99e0aae7SDavid Rees        "$is_statically_sized": True,
326*99e0aae7SDavid Rees        "$static_size_in_bits": size
327*99e0aae7SDavid Rees    }
328*99e0aae7SDavid Rees  requires_attr = ir_util.get_attribute(
329*99e0aae7SDavid Rees      referenced_type_definition.attribute, attributes.STATIC_REQUIREMENTS)
330*99e0aae7SDavid Rees  if requires_attr and not ir_util.constant_value(requires_attr.expression,
331*99e0aae7SDavid Rees                                                  bindings):
332*99e0aae7SDavid Rees    # TODO(bolms): Figure out a better way to build this error message.
333*99e0aae7SDavid Rees    # The "Requirements specified here." message should print out the actual
334*99e0aae7SDavid Rees    # source text of the requires attribute, so that should help, but it's still
335*99e0aae7SDavid Rees    # a bit generic and unfriendly.
336*99e0aae7SDavid Rees    return [[
337*99e0aae7SDavid Rees        error.error(
338*99e0aae7SDavid Rees            source_file_name, usage_source_location,
339*99e0aae7SDavid Rees            "Requirements of {} not met.".format(
340*99e0aae7SDavid Rees                type_ir.atomic_type.reference.canonical_name.object_path[-1])),
341*99e0aae7SDavid Rees        error.note(
342*99e0aae7SDavid Rees            type_ir.atomic_type.reference.canonical_name.module_file,
343*99e0aae7SDavid Rees            requires_attr.source_location,
344*99e0aae7SDavid Rees            "Requirements specified here.")
345*99e0aae7SDavid Rees    ]]
346*99e0aae7SDavid Rees  return []
347*99e0aae7SDavid Rees
348*99e0aae7SDavid Rees
349*99e0aae7SDavid Reesdef _check_allowed_in_bits(type_ir, type_definition, source_file_name, ir,
350*99e0aae7SDavid Rees                           errors):
351*99e0aae7SDavid Rees  if not type_ir.HasField("atomic_type"):
352*99e0aae7SDavid Rees    return
353*99e0aae7SDavid Rees  referenced_type_definition = ir_util.find_object(
354*99e0aae7SDavid Rees      type_ir.atomic_type.reference, ir)
355*99e0aae7SDavid Rees  if (type_definition.addressable_unit %
356*99e0aae7SDavid Rees      referenced_type_definition.addressable_unit != 0):
357*99e0aae7SDavid Rees    assert type_definition.addressable_unit == ir_data.AddressableUnit.BIT
358*99e0aae7SDavid Rees    assert (referenced_type_definition.addressable_unit ==
359*99e0aae7SDavid Rees            ir_data.AddressableUnit.BYTE)
360*99e0aae7SDavid Rees    errors.append([
361*99e0aae7SDavid Rees        error.error(source_file_name, type_ir.source_location,
362*99e0aae7SDavid Rees                    "Byte-oriented {} cannot be used in a bits field.".format(
363*99e0aae7SDavid Rees                        _render_type(type_ir, ir)))
364*99e0aae7SDavid Rees    ])
365*99e0aae7SDavid Rees
366*99e0aae7SDavid Rees
367*99e0aae7SDavid Reesdef _check_size_of_bits(type_ir, type_definition, source_file_name, errors):
368*99e0aae7SDavid Rees  """Checks that `bits` types are fixed size, less than 64 bits."""
369*99e0aae7SDavid Rees  del type_ir  # Unused
370*99e0aae7SDavid Rees  if type_definition.addressable_unit != ir_data.AddressableUnit.BIT:
371*99e0aae7SDavid Rees    return
372*99e0aae7SDavid Rees  fixed_size = ir_util.get_integer_attribute(
373*99e0aae7SDavid Rees      type_definition.attribute, attributes.FIXED_SIZE)
374*99e0aae7SDavid Rees  if fixed_size is None:
375*99e0aae7SDavid Rees    errors.append([error.error(source_file_name,
376*99e0aae7SDavid Rees                               type_definition.source_location,
377*99e0aae7SDavid Rees                               "`bits` types must be fixed size.")])
378*99e0aae7SDavid Rees    return
379*99e0aae7SDavid Rees  if fixed_size > 64:
380*99e0aae7SDavid Rees    errors.append([error.error(source_file_name,
381*99e0aae7SDavid Rees                               type_definition.source_location,
382*99e0aae7SDavid Rees                               "`bits` types must be 64 bits or smaller.")])
383*99e0aae7SDavid Rees
384*99e0aae7SDavid Rees
385*99e0aae7SDavid Rees_RESERVED_WORDS = None
386*99e0aae7SDavid Rees
387*99e0aae7SDavid Rees
388*99e0aae7SDavid Reesdef get_reserved_word_list():
389*99e0aae7SDavid Rees  if _RESERVED_WORDS is None:
390*99e0aae7SDavid Rees    _initialize_reserved_word_list()
391*99e0aae7SDavid Rees  return _RESERVED_WORDS
392*99e0aae7SDavid Rees
393*99e0aae7SDavid Rees
394*99e0aae7SDavid Reesdef _initialize_reserved_word_list():
395*99e0aae7SDavid Rees  global _RESERVED_WORDS
396*99e0aae7SDavid Rees  _RESERVED_WORDS = {}
397*99e0aae7SDavid Rees  language = None
398*99e0aae7SDavid Rees  for line in resources.load(
399*99e0aae7SDavid Rees      "compiler.front_end", "reserved_words").splitlines():
400*99e0aae7SDavid Rees    stripped_line = line.partition("#")[0].strip()
401*99e0aae7SDavid Rees    if not stripped_line:
402*99e0aae7SDavid Rees      continue
403*99e0aae7SDavid Rees    if stripped_line.startswith("--"):
404*99e0aae7SDavid Rees      language = stripped_line.partition("--")[2].strip()
405*99e0aae7SDavid Rees    else:
406*99e0aae7SDavid Rees      # For brevity's sake, only use the first language for error messages.
407*99e0aae7SDavid Rees      if stripped_line not in _RESERVED_WORDS:
408*99e0aae7SDavid Rees        _RESERVED_WORDS[stripped_line] = language
409*99e0aae7SDavid Rees
410*99e0aae7SDavid Rees
411*99e0aae7SDavid Reesdef _check_name_for_reserved_words(obj, source_file_name, errors, context_name):
412*99e0aae7SDavid Rees  if obj.name.name.text in get_reserved_word_list():
413*99e0aae7SDavid Rees    errors.append([
414*99e0aae7SDavid Rees        error.error(
415*99e0aae7SDavid Rees            source_file_name, obj.name.name.source_location,
416*99e0aae7SDavid Rees            "{} reserved word may not be used as {}.".format(
417*99e0aae7SDavid Rees                get_reserved_word_list()[obj.name.name.text],
418*99e0aae7SDavid Rees                context_name))
419*99e0aae7SDavid Rees    ])
420*99e0aae7SDavid Rees
421*99e0aae7SDavid Rees
422*99e0aae7SDavid Reesdef _check_field_name_for_reserved_words(field, source_file_name, errors):
423*99e0aae7SDavid Rees  return _check_name_for_reserved_words(field, source_file_name, errors,
424*99e0aae7SDavid Rees                                        "a field name")
425*99e0aae7SDavid Rees
426*99e0aae7SDavid Rees
427*99e0aae7SDavid Reesdef _check_enum_name_for_reserved_words(enum, source_file_name, errors):
428*99e0aae7SDavid Rees  return _check_name_for_reserved_words(enum, source_file_name, errors,
429*99e0aae7SDavid Rees                                        "an enum name")
430*99e0aae7SDavid Rees
431*99e0aae7SDavid Rees
432*99e0aae7SDavid Reesdef _check_type_name_for_reserved_words(type_definition, source_file_name,
433*99e0aae7SDavid Rees                                        errors):
434*99e0aae7SDavid Rees  return _check_name_for_reserved_words(
435*99e0aae7SDavid Rees      type_definition, source_file_name, errors, "a type name")
436*99e0aae7SDavid Rees
437*99e0aae7SDavid Rees
438*99e0aae7SDavid Reesdef _bounds_can_fit_64_bit_unsigned(minimum, maximum):
439*99e0aae7SDavid Rees  return minimum >= 0 and maximum <= 2**64 - 1
440*99e0aae7SDavid Rees
441*99e0aae7SDavid Rees
442*99e0aae7SDavid Reesdef _bounds_can_fit_64_bit_signed(minimum, maximum):
443*99e0aae7SDavid Rees  return minimum >= -(2**63) and maximum <= 2**63 - 1
444*99e0aae7SDavid Rees
445*99e0aae7SDavid Rees
446*99e0aae7SDavid Reesdef _bounds_can_fit_any_64_bit_integer_type(minimum, maximum):
447*99e0aae7SDavid Rees  return (_bounds_can_fit_64_bit_unsigned(minimum, maximum) or
448*99e0aae7SDavid Rees          _bounds_can_fit_64_bit_signed(minimum, maximum))
449*99e0aae7SDavid Rees
450*99e0aae7SDavid Rees
451*99e0aae7SDavid Reesdef _integer_bounds_errors_for_expression(expression, source_file_name):
452*99e0aae7SDavid Rees  """Checks that `expression` is in range for int64_t or uint64_t."""
453*99e0aae7SDavid Rees  # Only check non-constant subexpressions.
454*99e0aae7SDavid Rees  if (expression.WhichOneof("expression") == "function" and
455*99e0aae7SDavid Rees      not ir_util.is_constant_type(expression.type)):
456*99e0aae7SDavid Rees    errors = []
457*99e0aae7SDavid Rees    for arg in expression.function.args:
458*99e0aae7SDavid Rees      errors += _integer_bounds_errors_for_expression(arg, source_file_name)
459*99e0aae7SDavid Rees    if errors:
460*99e0aae7SDavid Rees      # Don't cascade bounds errors: report them at the lowest level they
461*99e0aae7SDavid Rees      # appear.
462*99e0aae7SDavid Rees      return errors
463*99e0aae7SDavid Rees  if expression.type.WhichOneof("type") == "integer":
464*99e0aae7SDavid Rees    errors = _integer_bounds_errors(expression.type.integer, "expression",
465*99e0aae7SDavid Rees                                    source_file_name,
466*99e0aae7SDavid Rees                                    expression.source_location)
467*99e0aae7SDavid Rees    if errors:
468*99e0aae7SDavid Rees      return errors
469*99e0aae7SDavid Rees  if (expression.WhichOneof("expression") == "function" and
470*99e0aae7SDavid Rees      not ir_util.is_constant_type(expression.type)):
471*99e0aae7SDavid Rees    int64_only_clauses = []
472*99e0aae7SDavid Rees    uint64_only_clauses = []
473*99e0aae7SDavid Rees    for clause in [expression] + list(expression.function.args):
474*99e0aae7SDavid Rees      if clause.type.WhichOneof("type") == "integer":
475*99e0aae7SDavid Rees        arg_minimum = int(clause.type.integer.minimum_value)
476*99e0aae7SDavid Rees        arg_maximum = int(clause.type.integer.maximum_value)
477*99e0aae7SDavid Rees        if not _bounds_can_fit_64_bit_signed(arg_minimum, arg_maximum):
478*99e0aae7SDavid Rees          uint64_only_clauses.append(clause)
479*99e0aae7SDavid Rees        elif not _bounds_can_fit_64_bit_unsigned(arg_minimum, arg_maximum):
480*99e0aae7SDavid Rees          int64_only_clauses.append(clause)
481*99e0aae7SDavid Rees    if int64_only_clauses and uint64_only_clauses:
482*99e0aae7SDavid Rees      error_set = [
483*99e0aae7SDavid Rees          error.error(
484*99e0aae7SDavid Rees              source_file_name, expression.source_location,
485*99e0aae7SDavid Rees              "Either all arguments to '{}' and its result must fit in a "
486*99e0aae7SDavid Rees              "64-bit unsigned integer, or all must fit in a 64-bit signed "
487*99e0aae7SDavid Rees              "integer.".format(expression.function.function_name.text))
488*99e0aae7SDavid Rees      ]
489*99e0aae7SDavid Rees      for signedness, clause_list in (("unsigned", uint64_only_clauses),
490*99e0aae7SDavid Rees                                      ("signed", int64_only_clauses)):
491*99e0aae7SDavid Rees        for clause in clause_list:
492*99e0aae7SDavid Rees          error_set.append(error.note(
493*99e0aae7SDavid Rees              source_file_name, clause.source_location,
494*99e0aae7SDavid Rees              "Requires {} 64-bit integer.".format(signedness)))
495*99e0aae7SDavid Rees      return [error_set]
496*99e0aae7SDavid Rees  return []
497*99e0aae7SDavid Rees
498*99e0aae7SDavid Rees
499*99e0aae7SDavid Reesdef _integer_bounds_errors(bounds, name, source_file_name,
500*99e0aae7SDavid Rees                           error_source_location):
501*99e0aae7SDavid Rees  """Returns appropriate errors, if any, for the given integer bounds."""
502*99e0aae7SDavid Rees  assert bounds.minimum_value, "{}".format(bounds)
503*99e0aae7SDavid Rees  assert bounds.maximum_value, "{}".format(bounds)
504*99e0aae7SDavid Rees  if (bounds.minimum_value == "-infinity" or
505*99e0aae7SDavid Rees      bounds.maximum_value == "infinity"):
506*99e0aae7SDavid Rees    return [[
507*99e0aae7SDavid Rees        error.error(
508*99e0aae7SDavid Rees            source_file_name, error_source_location,
509*99e0aae7SDavid Rees            "Integer range of {} must not be unbounded; it must fit "
510*99e0aae7SDavid Rees            "in a 64-bit signed or unsigned integer.".format(name))
511*99e0aae7SDavid Rees    ]]
512*99e0aae7SDavid Rees  if not _bounds_can_fit_any_64_bit_integer_type(int(bounds.minimum_value),
513*99e0aae7SDavid Rees                                                 int(bounds.maximum_value)):
514*99e0aae7SDavid Rees    if int(bounds.minimum_value) == int(bounds.maximum_value):
515*99e0aae7SDavid Rees      return [[
516*99e0aae7SDavid Rees          error.error(
517*99e0aae7SDavid Rees              source_file_name, error_source_location,
518*99e0aae7SDavid Rees              "Constant value {} of {} cannot fit in a 64-bit signed or "
519*99e0aae7SDavid Rees              "unsigned integer.".format(bounds.minimum_value, name))
520*99e0aae7SDavid Rees      ]]
521*99e0aae7SDavid Rees    else:
522*99e0aae7SDavid Rees      return [[
523*99e0aae7SDavid Rees          error.error(
524*99e0aae7SDavid Rees              source_file_name, error_source_location,
525*99e0aae7SDavid Rees              "Potential range of {} is {} to {}, which cannot fit "
526*99e0aae7SDavid Rees              "in a 64-bit signed or unsigned integer.".format(
527*99e0aae7SDavid Rees                  name, bounds.minimum_value, bounds.maximum_value))
528*99e0aae7SDavid Rees      ]]
529*99e0aae7SDavid Rees  return []
530*99e0aae7SDavid Rees
531*99e0aae7SDavid Rees
532*99e0aae7SDavid Reesdef _check_bounds_on_runtime_integer_expressions(expression, source_file_name,
533*99e0aae7SDavid Rees                                                 in_attribute, errors):
534*99e0aae7SDavid Rees  if in_attribute and in_attribute.name.text == attributes.STATIC_REQUIREMENTS:
535*99e0aae7SDavid Rees    # [static_requirements] is never evaluated at runtime, and $size_in_bits is
536*99e0aae7SDavid Rees    # unbounded, so it should not be checked.
537*99e0aae7SDavid Rees    return
538*99e0aae7SDavid Rees  # The logic for gathering errors and suppressing cascades is simpler if
539*99e0aae7SDavid Rees  # errors are just returned, rather than appended to a shared list.
540*99e0aae7SDavid Rees  errors += _integer_bounds_errors_for_expression(expression, source_file_name)
541*99e0aae7SDavid Rees
542*99e0aae7SDavid Reesdef _attribute_in_attribute_action(a):
543*99e0aae7SDavid Rees  return {"in_attribute": a}
544*99e0aae7SDavid Rees
545*99e0aae7SDavid Reesdef check_constraints(ir):
546*99e0aae7SDavid Rees  """Checks miscellaneous validity constraints in ir.
547*99e0aae7SDavid Rees
548*99e0aae7SDavid Rees  Checks that auto array sizes are only used for the outermost size of
549*99e0aae7SDavid Rees  multidimensional arrays.  That is, Type[3][] is OK, but Type[][3] is not.
550*99e0aae7SDavid Rees
551*99e0aae7SDavid Rees  Checks that fixed-size fields are a correct size to hold statically-sized
552*99e0aae7SDavid Rees  types.
553*99e0aae7SDavid Rees
554*99e0aae7SDavid Rees  Checks that inner array dimensions are constant.
555*99e0aae7SDavid Rees
556*99e0aae7SDavid Rees  Checks that only constant-size types are used in arrays.
557*99e0aae7SDavid Rees
558*99e0aae7SDavid Rees  Arguments:
559*99e0aae7SDavid Rees    ir: An ir_data.EmbossIr object to check.
560*99e0aae7SDavid Rees
561*99e0aae7SDavid Rees  Returns:
562*99e0aae7SDavid Rees    A list of ConstraintViolations, or an empty list if there are none.
563*99e0aae7SDavid Rees  """
564*99e0aae7SDavid Rees  errors = []
565*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
566*99e0aae7SDavid Rees      ir, [ir_data.Structure, ir_data.Type], _check_allowed_in_bits,
567*99e0aae7SDavid Rees      parameters={"errors": errors})
568*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
569*99e0aae7SDavid Rees      # TODO(bolms): look for [ir_data.ArrayType], [ir_data.AtomicType], and
570*99e0aae7SDavid Rees      # simplify _check_that_array_base_types_are_fixed_size.
571*99e0aae7SDavid Rees      ir, [ir_data.ArrayType], _check_that_array_base_types_are_fixed_size,
572*99e0aae7SDavid Rees      parameters={"errors": errors})
573*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
574*99e0aae7SDavid Rees      ir, [ir_data.Structure, ir_data.ArrayType],
575*99e0aae7SDavid Rees      _check_that_array_base_types_in_structs_are_multiples_of_bytes,
576*99e0aae7SDavid Rees      parameters={"errors": errors})
577*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
578*99e0aae7SDavid Rees      ir, [ir_data.ArrayType, ir_data.ArrayType],
579*99e0aae7SDavid Rees      _check_that_inner_array_dimensions_are_constant,
580*99e0aae7SDavid Rees      parameters={"errors": errors})
581*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
582*99e0aae7SDavid Rees      ir, [ir_data.Structure], _check_size_of_bits,
583*99e0aae7SDavid Rees      parameters={"errors": errors})
584*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
585*99e0aae7SDavid Rees      ir, [ir_data.Structure, ir_data.Type], _check_type_requirements_for_field,
586*99e0aae7SDavid Rees      parameters={"errors": errors})
587*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
588*99e0aae7SDavid Rees      ir, [ir_data.Field], _check_field_name_for_reserved_words,
589*99e0aae7SDavid Rees      parameters={"errors": errors})
590*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
591*99e0aae7SDavid Rees      ir, [ir_data.EnumValue], _check_enum_name_for_reserved_words,
592*99e0aae7SDavid Rees      parameters={"errors": errors})
593*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
594*99e0aae7SDavid Rees      ir, [ir_data.TypeDefinition], _check_type_name_for_reserved_words,
595*99e0aae7SDavid Rees      parameters={"errors": errors})
596*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
597*99e0aae7SDavid Rees      ir, [ir_data.Expression], _check_constancy_of_constant_references,
598*99e0aae7SDavid Rees      parameters={"errors": errors})
599*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
600*99e0aae7SDavid Rees      ir, [ir_data.Enum], _check_that_enum_values_are_representable,
601*99e0aae7SDavid Rees      parameters={"errors": errors})
602*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
603*99e0aae7SDavid Rees      ir, [ir_data.Expression], _check_bounds_on_runtime_integer_expressions,
604*99e0aae7SDavid Rees      incidental_actions={ir_data.Attribute: _attribute_in_attribute_action},
605*99e0aae7SDavid Rees      skip_descendants_of={ir_data.EnumValue, ir_data.Expression},
606*99e0aae7SDavid Rees      parameters={"errors": errors, "in_attribute": None})
607*99e0aae7SDavid Rees  traverse_ir.fast_traverse_ir_top_down(
608*99e0aae7SDavid Rees      ir, [ir_data.RuntimeParameter],
609*99e0aae7SDavid Rees      _check_type_requirements_for_parameter_type,
610*99e0aae7SDavid Rees      parameters={"errors": errors})
611*99e0aae7SDavid Rees  return errors
612