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