Skip to content

Commit 75bdc4c

Browse files
authored
Add error message for obscure case with parameters. (#214)
Prior to this change, if a user omitted the length of an integer parameter *and* used that parameter as an argument to an operator or function, the compiler with throw an `AttributeError` instead of providing a useful message. This is because the check that integer runtime parameters have an explicit size happened later than the function in `expression_bounds.py` that tried to use the size. This change moves the check earlier (incidentally spliting `check_constraints` into `check_constraints` and `check_early_constraints`), and also gives it a better error message.
1 parent 35220b7 commit 75bdc4c

File tree

4 files changed

+99
-26
lines changed

4 files changed

+99
-26
lines changed

compiler/front_end/constraints.py

Lines changed: 55 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,49 @@ def _check_type_requirements_for_field(
319319
)
320320

321321

322+
def _check_early_type_requirements_for_parameter_type(
323+
runtime_parameter, ir, source_file_name, errors
324+
):
325+
"""Checks that the type of a parameter is valid."""
326+
physical_type = runtime_parameter.physical_type_alias
327+
logical_type = runtime_parameter.type
328+
size = ir_util.constant_value(physical_type.size_in_bits)
329+
if logical_type.which_type == "integer":
330+
# This seems a little weird: for `UInt`, `Int`, etc., the explicit size
331+
# is required, but for enums it is banned. This is because enums have
332+
# a "natural" size (the size specified in the `enum` definition, or 64
333+
# bits by default) in expressions, so the physical size would just be
334+
# ignored. Integer types do not have "natural" sizes, so the width is
335+
# required.
336+
if not physical_type.HasField("size_in_bits"):
337+
errors.extend(
338+
[
339+
[
340+
error.error(
341+
source_file_name,
342+
physical_type.source_location,
343+
f"Parameters with integer type must have explicit size (e.g., `{physical_type.atomic_type.reference.source_name[-1].text}:32`).",
344+
)
345+
]
346+
]
347+
)
348+
elif logical_type.which_type == "enumeration":
349+
if physical_type.HasField("size_in_bits"):
350+
errors.extend(
351+
[
352+
[
353+
error.error(
354+
source_file_name,
355+
physical_type.size_in_bits.source_location,
356+
"Parameters with enum type may not have explicit size.",
357+
)
358+
]
359+
]
360+
)
361+
else:
362+
assert False, "Non-integer/enum parameters should have been caught earlier."
363+
364+
322365
def _check_type_requirements_for_parameter_type(
323366
runtime_parameter, ir, source_file_name, errors
324367
):
@@ -346,22 +389,7 @@ def _check_type_requirements_for_parameter_type(
346389
)
347390
)
348391
elif logical_type.which_type == "enumeration":
349-
if physical_type.HasField("size_in_bits"):
350-
# This seems a little weird: for `UInt`, `Int`, etc., the explicit size is
351-
# required, but for enums it is banned. This is because enums have a
352-
# "native" 64-bit size in expressions, so the physical size is just
353-
# ignored.
354-
errors.extend(
355-
[
356-
[
357-
error.error(
358-
source_file_name,
359-
physical_type.size_in_bits.source_location,
360-
"Parameters with enum type may not have explicit size.",
361-
)
362-
]
363-
]
364-
)
392+
pass
365393
else:
366394
assert False, "Non-integer/enum parameters should have been caught earlier."
367395

@@ -686,6 +714,17 @@ def _attribute_in_attribute_action(a):
686714
return {"in_attribute": a}
687715

688716

717+
def check_early_constraints(ir):
718+
errors = []
719+
traverse_ir.fast_traverse_ir_top_down(
720+
ir,
721+
[ir_data.RuntimeParameter],
722+
_check_early_type_requirements_for_parameter_type,
723+
parameters={"errors": errors},
724+
)
725+
return errors
726+
727+
689728
def check_constraints(ir):
690729
"""Checks miscellaneous validity constraints in ir.
691730

compiler/front_end/constraints_test.py

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,11 @@
2424
from compiler.util import test_util
2525

2626

27-
def _make_ir_from_emb(emb_text, name="m.emb"):
27+
def _make_ir_from_emb(emb_text, name="m.emb", stop_before_step="check_constraints"):
2828
ir, unused_debug_info, errors = glue.parse_emboss_file(
2929
name,
3030
test_util.dict_file_reader({name: emb_text}),
31-
stop_before_step="check_constraints",
31+
stop_before_step=stop_before_step,
3232
)
3333
assert not errors, repr(errors)
3434
return ir
@@ -1252,8 +1252,12 @@ def test_checks_constancy_of_constant_references(self):
12521252
error.filter_errors(constraints.check_constraints(ir)),
12531253
)
12541254

1255-
def test_checks_for_explicit_size_on_parameters(self):
1256-
ir = _make_ir_from_emb("struct Foo(y: UInt):\n" " 0 [+1] UInt x\n")
1255+
def test_checks_for_explicit_integer_size_on_parameters(self):
1256+
ir = _make_ir_from_emb(
1257+
"struct Foo(y: UInt):\n" #
1258+
" 0 [+1] UInt x\n",
1259+
stop_before_step="check_early_constraints",
1260+
)
12571261
error_parameter = ir.module[0].type[0].runtime_parameter[0]
12581262
error_location = error_parameter.physical_type_alias.source_location
12591263
self.assertEqual(
@@ -1262,12 +1266,34 @@ def test_checks_for_explicit_size_on_parameters(self):
12621266
error.error(
12631267
"m.emb",
12641268
error_location,
1265-
"Integer range of parameter must not be unbounded; it "
1266-
"must fit in a 64-bit signed or unsigned integer.",
1269+
"Parameters with integer type must have explicit size (e.g., `UInt:32`).",
12671270
)
12681271
]
12691272
],
1270-
error.filter_errors(constraints.check_constraints(ir)),
1273+
error.filter_errors(constraints.check_early_constraints(ir)),
1274+
)
1275+
1276+
def test_checks_for_explicit_integer_size_on_parameters_and_uses_type_in_error(
1277+
self,
1278+
):
1279+
ir = _make_ir_from_emb(
1280+
"struct Foo(y: Int):\n" #
1281+
" 0 [+1] UInt x\n",
1282+
stop_before_step="check_early_constraints",
1283+
)
1284+
error_parameter = ir.module[0].type[0].runtime_parameter[0]
1285+
error_location = error_parameter.physical_type_alias.source_location
1286+
self.assertEqual(
1287+
[
1288+
[
1289+
error.error(
1290+
"m.emb",
1291+
error_location,
1292+
"Parameters with integer type must have explicit size (e.g., `Int:32`).",
1293+
)
1294+
]
1295+
],
1296+
error.filter_errors(constraints.check_early_constraints(ir)),
12711297
)
12721298

12731299
def test_checks_for_correct_explicit_size_on_parameters(self):
@@ -1292,7 +1318,11 @@ def test_checks_for_correct_explicit_size_on_parameters(self):
12921318

12931319
def test_checks_for_explicit_enum_size_on_parameters(self):
12941320
ir = _make_ir_from_emb(
1295-
"struct Foo(y: Bar:8):\n" " 0 [+1] UInt x\n" "enum Bar:\n" " QUX = 1\n"
1321+
"struct Foo(y: Bar:8):\n" #
1322+
" 0 [+1] UInt x\n"
1323+
"enum Bar:\n"
1324+
" QUX = 1\n",
1325+
stop_before_step="check_early_constraints",
12961326
)
12971327
error_parameter = ir.module[0].type[0].runtime_parameter[0]
12981328
error_size = error_parameter.physical_type_alias.size_in_bits
@@ -1307,7 +1337,7 @@ def test_checks_for_explicit_enum_size_on_parameters(self):
13071337
)
13081338
]
13091339
],
1310-
error.filter_errors(constraints.check_constraints(ir)),
1340+
error.filter_errors(constraints.check_early_constraints(ir)),
13111341
)
13121342

13131343

compiler/front_end/expression_bounds.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,18 +144,21 @@ def _compute_constraints_of_field_reference(expression, ir):
144144
expression.type.integer.modulus = "1"
145145
expression.type.integer.modular_value = "0"
146146
type_definition = ir_util.find_parent_object(field_path, ir)
147+
type_size = None
147148
if isinstance(field, ir_data.Field):
148149
referrent_type = field.type
149150
else:
150151
referrent_type = field.physical_type_alias
151152
if referrent_type.HasField("size_in_bits"):
152153
type_size = ir_util.constant_value(referrent_type.size_in_bits)
153-
else:
154+
elif isinstance(field, ir_data.Field):
154155
field_size = ir_util.constant_value(field.location.size)
155156
if field_size is None:
156157
type_size = None
157158
else:
158159
type_size = field_size * type_definition.addressable_unit
160+
else:
161+
type_size = None
159162
assert referrent_type.HasField("atomic_type"), field
160163
assert not referrent_type.atomic_type.reference.canonical_name.module_file
161164
_set_integer_constraints_from_physical_type(

compiler/front_end/glue.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,7 @@ def process_ir(ir, stop_before_step):
319319
symbol_resolver.resolve_field_references,
320320
type_check.annotate_types,
321321
type_check.check_types,
322+
constraints.check_early_constraints,
322323
expression_bounds.compute_constants,
323324
attribute_checker.normalize_and_verify,
324325
constraints.check_constraints,

0 commit comments

Comments
 (0)