blob: ba7ceb71fdb99c885a84a81e77e5a7893f83e5fc [file] [log] [blame]
# Copyright 2019 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for dependency_checker.py."""
import unittest
from compiler.front_end import dependency_checker
from compiler.front_end import glue
from compiler.front_end import test_util
from compiler.util import error
def _parse_snippet(emb_file):
ir, unused_debug_info, errors = glue.parse_emboss_file(
"m.emb",
test_util.dict_file_reader({"m.emb": emb_file}),
stop_before_step="find_dependency_cycles")
assert not errors
return ir
def _find_dependencies_for_snippet(emb_file):
ir, unused_debug_info, errors = glue.parse_emboss_file(
"m.emb",
test_util.dict_file_reader({
"m.emb": emb_file
}),
stop_before_step="set_dependency_order")
assert not errors, errors
return ir
class DependencyCheckerTest(unittest.TestCase):
def test_error_on_simple_field_cycle(self):
ir = _parse_snippet("struct Foo:\n"
" 0 [+field2] UInt field1\n"
" 0 [+field1] UInt field2\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nfield1"),
error.note("m.emb", struct.field[1].source_location, "field2")
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_self_cycle(self):
ir = _parse_snippet("struct Foo:\n"
" 0 [+field1] UInt field1\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nfield1")
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_triple_field_cycle(self):
ir = _parse_snippet("struct Foo:\n"
" 0 [+field2] UInt field1\n"
" 0 [+field3] UInt field2\n"
" 0 [+field1] UInt field3\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nfield1"),
error.note("m.emb", struct.field[1].source_location, "field2"),
error.note("m.emb", struct.field[2].source_location, "field3"),
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_complex_field_cycle(self):
ir = _parse_snippet("struct Foo:\n"
" 0 [+field2] UInt field1\n"
" 0 [+field3+field4] UInt field2\n"
" 0 [+field1] UInt field3\n"
" 0 [+field2] UInt field4\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nfield1"),
error.note("m.emb", struct.field[1].source_location, "field2"),
error.note("m.emb", struct.field[2].source_location, "field3"),
error.note("m.emb", struct.field[3].source_location, "field4"),
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_simple_enum_value_cycle(self):
ir = _parse_snippet("enum Foo:\n"
" XX = YY\n"
" YY = XX\n")
enum = ir.module[0].type[0].enumeration
self.assertEqual([[
error.error("m.emb", enum.value[0].source_location,
"Dependency cycle\nXX"),
error.note("m.emb", enum.value[1].source_location, "YY")
]], dependency_checker.find_dependency_cycles(ir))
def test_no_error_on_no_cycle(self):
ir = _parse_snippet("enum Foo:\n"
" XX = 0\n"
" YY = XX\n")
self.assertEqual([], dependency_checker.find_dependency_cycles(ir))
def test_error_on_cycle_nested(self):
ir = _parse_snippet("struct Foo:\n"
" struct Bar:\n"
" 0 [+field2] UInt field1\n"
" 0 [+field1] UInt field2\n"
" 0 [+1] UInt field\n")
struct = ir.module[0].type[0].subtype[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nfield1"),
error.note("m.emb", struct.field[1].source_location, "field2")
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_import_cycle(self):
ir, unused_debug_info, errors = glue.parse_emboss_file(
"m.emb",
test_util.dict_file_reader({"m.emb": 'import "n.emb" as n\n',
"n.emb": 'import "m.emb" as m\n'}),
stop_before_step="find_dependency_cycles")
assert not errors
self.assertEqual([[
error.error("m.emb", ir.module[0].source_location,
"Import dependency cycle\nm.emb"),
error.note("n.emb", ir.module[2].source_location, "n.emb")
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_import_cycle_and_field_cycle(self):
ir, unused_debug_info, errors = glue.parse_emboss_file(
"m.emb",
test_util.dict_file_reader({"m.emb": 'import "n.emb" as n\n'
"struct Foo:\n"
" 0 [+field1] UInt field1\n",
"n.emb": 'import "m.emb" as m\n'}),
stop_before_step="find_dependency_cycles")
assert not errors
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", ir.module[0].source_location,
"Import dependency cycle\nm.emb"),
error.note("n.emb", ir.module[2].source_location, "n.emb")
], [
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nfield1")
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_field_existence_self_cycle(self):
ir = _parse_snippet("struct Foo:\n"
" if x == 1:\n"
" 0 [+1] UInt x\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nx")
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_field_existence_cycle(self):
ir = _parse_snippet("struct Foo:\n"
" if y == 1:\n"
" 0 [+1] UInt x\n"
" if x == 0:\n"
" 1 [+1] UInt y\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nx"),
error.note("m.emb", struct.field[1].source_location, "y")
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_virtual_field_cycle(self):
ir = _parse_snippet("struct Foo:\n"
" let x = y\n"
" let y = x\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nx"),
error.note("m.emb", struct.field[1].source_location, "y")
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_virtual_non_virtual_field_cycle(self):
ir = _parse_snippet("struct Foo:\n"
" let x = y\n"
" x [+4] UInt y\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nx"),
error.note("m.emb", struct.field[1].source_location, "y")
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_non_virtual_virtual_field_cycle(self):
ir = _parse_snippet("struct Foo:\n"
" y [+4] UInt x\n"
" let y = x\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nx"),
error.note("m.emb", struct.field[1].source_location, "y")
]], dependency_checker.find_dependency_cycles(ir))
def test_error_on_cycle_involving_subfield(self):
ir = _parse_snippet("struct Bar:\n"
" foo_b.x [+4] Foo foo_a\n"
" foo_a.x [+4] Foo foo_b\n"
"struct Foo:\n"
" 0 [+4] UInt x\n")
struct = ir.module[0].type[0].structure
self.assertEqual([[
error.error("m.emb", struct.field[0].source_location,
"Dependency cycle\nfoo_a"),
error.note("m.emb", struct.field[1].source_location, "foo_b")
]], dependency_checker.find_dependency_cycles(ir))
def test_dependency_ordering_with_no_dependencies(self):
ir = _find_dependencies_for_snippet("struct Foo:\n"
" 0 [+4] UInt a\n"
" 4 [+4] UInt b\n")
self.assertEqual([], dependency_checker.set_dependency_order(ir))
struct = ir.module[0].type[0].structure
self.assertEqual([0, 1], struct.fields_in_dependency_order[:2])
def test_dependency_ordering_with_dependency_in_order(self):
ir = _find_dependencies_for_snippet("struct Foo:\n"
" 0 [+4] UInt a\n"
" a [+4] UInt b\n")
self.assertEqual([], dependency_checker.set_dependency_order(ir))
struct = ir.module[0].type[0].structure
self.assertEqual([0, 1], struct.fields_in_dependency_order[:2])
def test_dependency_ordering_with_dependency_in_reverse_order(self):
ir = _find_dependencies_for_snippet("struct Foo:\n"
" b [+4] UInt a\n"
" 0 [+4] UInt b\n")
self.assertEqual([], dependency_checker.set_dependency_order(ir))
struct = ir.module[0].type[0].structure
self.assertEqual([1, 0], struct.fields_in_dependency_order[:2])
def test_dependency_ordering_with_extra_fields(self):
ir = _find_dependencies_for_snippet("struct Foo:\n"
" d [+4] UInt a\n"
" 4 [+4] UInt b\n"
" 8 [+4] UInt c\n"
" 12 [+4] UInt d\n")
self.assertEqual([], dependency_checker.set_dependency_order(ir))
struct = ir.module[0].type[0].structure
self.assertEqual([1, 2, 3, 0], struct.fields_in_dependency_order[:4])
def test_dependency_ordering_scrambled(self):
ir = _find_dependencies_for_snippet("struct Foo:\n"
" d [+4] UInt a\n"
" c [+4] UInt b\n"
" a [+4] UInt c\n"
" 12 [+4] UInt d\n")
self.assertEqual([], dependency_checker.set_dependency_order(ir))
struct = ir.module[0].type[0].structure
self.assertEqual([3, 0, 2, 1], struct.fields_in_dependency_order[:4])
def test_dependency_ordering_multiple_dependents(self):
ir = _find_dependencies_for_snippet("struct Foo:\n"
" d [+4] UInt a\n"
" d [+4] UInt b\n"
" d [+4] UInt c\n"
" 12 [+4] UInt d\n")
self.assertEqual([], dependency_checker.set_dependency_order(ir))
struct = ir.module[0].type[0].structure
self.assertEqual([3, 0, 1, 2], struct.fields_in_dependency_order[:4])
def test_dependency_ordering_multiple_dependencies(self):
ir = _find_dependencies_for_snippet("struct Foo:\n"
" b+c [+4] UInt a\n"
" 4 [+4] UInt b\n"
" 8 [+4] UInt c\n"
" a [+4] UInt d\n")
self.assertEqual([], dependency_checker.set_dependency_order(ir))
struct = ir.module[0].type[0].structure
self.assertEqual([1, 2, 0, 3], struct.fields_in_dependency_order[:4])
def test_dependency_ordering_with_parameter(self):
ir = _find_dependencies_for_snippet("struct Foo:\n"
" 0 [+1] Bar(x) b\n"
" 1 [+1] UInt x\n"
"struct Bar(x: UInt:8):\n"
" x [+1] UInt y\n")
self.assertEqual([], dependency_checker.set_dependency_order(ir))
struct = ir.module[0].type[0].structure
self.assertEqual([1, 0], struct.fields_in_dependency_order[:2])
def test_dependency_ordering_with_local_parameter(self):
ir = _find_dependencies_for_snippet("struct Foo(x: Int:13):\n"
" 0 [+x] Int b\n")
self.assertEqual([], dependency_checker.set_dependency_order(ir))
struct = ir.module[0].type[0].structure
self.assertEqual([0], struct.fields_in_dependency_order[:1])
if __name__ == "__main__":
unittest.main()