Esempio n. 1
0
    def _typecheck(self,
                   text: Text,
                   error: Optional[Text] = None,
                   error_type=XlsTypeError):
        """Checks the first function in "text" for type errors.

    Args:
      text: Text to parse.
      error: Whether it is expected that the text will cause a type error.
      error_type: Type of error to check for, if "error" is given.
    """
        filename = '/fake/test_module.x'
        with fakefs_test_util.scoped_fakefs(filename, text):
            try:
                m = parser_helpers.parse_text(text,
                                              'test_module',
                                              print_on_error=True,
                                              filename=filename)
            except cpp_parser.CppParseError as e:
                parser_helpers.pprint_positional_error(e)
                raise

            if error:
                with self.assertRaises(error_type) as cm:
                    typecheck.check_module(m, f_import=None)
                self.assertIn(error, str(cm.exception))
            else:
                try:
                    typecheck.check_module(m, f_import=None)
                except (span.PositionalError, cpp_parser.CppParseError) as e:
                    parser_helpers.pprint_positional_error(e)
                    raise
 def _get_module(
         self, program: Text) -> Tuple[ast.Module, type_info_mod.TypeInfo]:
     filename = '/fake/test_program.x'
     with fakefs_test_util.scoped_fakefs(filename, program):
         m, type_info = parse_and_typecheck.parse_text(program,
                                                       'test_program',
                                                       print_on_error=True,
                                                       f_import=None,
                                                       filename=filename)
         return m, type_info
Esempio n. 3
0
 def test_bad_dim_expression(self):
     with self.assertRaises(parser.CppParseError):
         program = """
     fn [X: u32, Y: u32] foo(x: bits[X + Y]) -> bits[5] { u5:5 }
   """
         with fakefs_test_util.scoped_fakefs(self.fake_filename, program):
             parser_helpers.parse_text(program,
                                       name=self.fake_filename,
                                       print_on_error=True,
                                       filename=self.fake_filename)
Esempio n. 4
0
 def test_bad_dim(self):
     with self.assertRaises(parser.ParseError):
         program = """
     fn foo(x: bits[+]) -> bits[5] { u5:5 }
   """
         with fakefs_test_util.scoped_fakefs(self.fake_filename, program):
             parser_helpers.parse_text(program,
                                       name=self.fake_filename,
                                       print_on_error=True,
                                       filename=self.fake_filename)
Esempio n. 5
0
 def test_invalid_parameter_cast(self):
     program = """
 fn [N: u32]addN(x: u32) -> u32 {
   x + u32: N
 }
 """
     with fakefs_test_util.scoped_fakefs(self.fake_filename, program):
         with self.assertRaises(CppParseError):
             parser_helpers.parse_text(program,
                                       name=self.fake_filename,
                                       print_on_error=True,
                                       filename=self.fake_filename)
Esempio n. 6
0
 def _get_module(self,
                 program: Text) -> Tuple[ast.Module, type_info_mod.TypeInfo]:
   filename = '/fake/test_program.x'
   with fakefs_test_util.scoped_fakefs(filename, program):
     m, type_info = parse_and_typecheck.parse_text(
         program,
         'test_program',
         print_on_error=True,
         import_cache=ImportCache(),
         additional_search_paths=(),
         filename=filename)
     return m, type_info
Esempio n. 7
0
 def _parse_internal(
     self, program: Text, bindings: Optional[parser.Bindings],
     fparse: Callable[[parser.Parser, parser.Bindings],
                      TypeVar('T')]
 ) -> TypeVar('T'):
     with fakefs_test_util.scoped_fakefs(self.fake_filename, program):
         s = scanner.Scanner(self.fake_filename, program)
         b = bindings or parser.Bindings(None)
         try:
             e = fparse(parser.Parser(s, 'test_module'), b)
         except parser.CppParseError as e:
             parser_helpers.pprint_positional_error(e)
             raise
         self.assertTrue(s.at_eof())
         return e
Esempio n. 8
0
 def test_generates_valid_functions(self):
   g = ast_generator.AstGenerator(
       random.Random(0), ast_generator.AstGeneratorOptions())
   for i in range(32):
     print('Generating sample', i)
     _, m = g.generate_function_in_module('main', 'test')
     text = str(m)
     filename = '/fake/test_sample.x'
     with fakefs_test_util.scoped_fakefs(filename, text):
       try:
         module = parser_helpers.parse_text(
             text, name='test_sample', print_on_error=True, filename=filename)
         typecheck.check_module(module, f_import=None)
       except PositionalError as e:
         parser_helpers.pprint_positional_error(e)
         raise
Esempio n. 9
0
    def test_parser_errors(self):
        with self.assertRaises(parser.CppParseError):
            program = """
      type Tuple2 = (u32, u32);

      fn tuple_assign(x: u32, y: u32) -> (u32) {
        // We don't have compound expressions yet, so the use of
        // curly braces should result in an error.
        let (i, i): Tuple2 = (x, y);
        {
             i
        }
      }"""
            with fakefs_test_util.scoped_fakefs(self.fake_filename, program):
                parser_helpers.parse_text(program,
                                          name=self.fake_filename,
                                          print_on_error=True,
                                          filename=self.fake_filename)
Esempio n. 10
0
    def test_sign_convert_args_batch(self):
        dslx_text = 'fn main(y: s8) -> s8 { y }'
        filename = '/fake/test_module.x'
        with fakefs_test_util.scoped_fakefs(filename, dslx_text):
            m = parser_helpers.parse_text(dslx_text,
                                          'test_module',
                                          print_on_error=True,
                                          filename=filename)

        f = m.get_function('main')
        self.assertEqual(
            sample_runner.sign_convert_args_batch(
                f, m, ((Value.make_ubits(8, 2), ), )),
            ((Value.make_sbits(8, 2), ), ))
        # Converting a value which is already signed is no problem.
        self.assertEqual(
            sample_runner.sign_convert_args_batch(
                f, m, ((Value.make_sbits(8, 2), ), )),
            ((Value.make_sbits(8, 2), ), ))
Esempio n. 11
0
  def test_pprint_parse_error(self):
    output = io.StringIO()
    filename = '/fake/test_file.x'
    text = 'oh\nwhoops\nI did an\nerror somewhere\nthat is bad'
    with fakefs_test_util.scoped_fakefs(filename, text):
      pos = scanner.Pos(filename, lineno=2, colno=0)
      span = Span(pos, pos.bump_col())
      error = parser.ParseError(span, 'This is bad')
      parser_helpers.pprint_positional_error(
          error,
          output=cast(io.IOBase, output),
          color=False,
          error_context_line_count=3)

    expected = textwrap.dedent("""\
    /fake/test_file.x:2-4
      0002: whoops
    * 0003: I did an
            ^^ This is bad @ /fake/test_file.x:3:1
      0004: error somewhere
    """)
    self.assertMultiLineEqual(expected, output.getvalue())
Esempio n. 12
0
    def test_co_recursion(self):
        with self.assertRaises(CppParseError) as cm:
            program = """
        // Co-recursion
        //
        // Also illegal. Can be found by finding cycles in the call graph.
        //
        fn foo(x: u32) -> u32 {
          let y: u32 = 7;
          bar(x - 1, y)
        }

        fn bar(x: u32, y: u32) -> u32 {
          let z: u32 = 11;
          foo(x - 1 + y + z)
        }
        """
            with fakefs_test_util.scoped_fakefs(self.fake_filename, program):
                parser_helpers.parse_text(program,
                                          name=self.fake_filename,
                                          print_on_error=True,
                                          filename=self.fake_filename)
        self.assertIn("Cannot find a definition for name: 'bar'",
                      str(cm.exception))
Esempio n. 13
0
    def test_invalid_constructs(self):
        # TODO(rhundt): Over time these tests will fail at parse time or
        # runtime. At this point, we will have to check the relevant
        # errors and handle them properly.
        program = """
    // A handful of language constructs that could be considered harmful, borderline
    // illegal, or definitely illegal. They could be allowed, warned about, or
    // be reported as errors. This file collects all such cases and it will be
    // used in testing, once the semantics have been clarified. As new cases will
    // emerge, they should be added to this file as well.

    // Parameter re-definition.
    //
    // This is certainly allowed in imperative languages, and even Haskell allows
    // the following expression:
    //   f x = let x = 3 in x * x
    // No matter what you pass to this function, it will always return 9.
    //
    // The question is whether or not to warn on this, perhaps we should warn
    // optionally?
    //
    fn param_redefine(x: u32) -> u32 {
      let x: u32 = 3;
        x * x
    }

    // Dead code
    //
    // The following code is legal, but is probably not what the user intended.
    // We should emit a warning, perhaps optionally.
    //
    fn dead_code(x: u32) -> u32 {
      let y: u32 = 3;
      let y: u32 = 4;
      x * y
    }

    // Unused code
    //
    // The following code is legal, but contains an unused expression.
    // Languages like Go warn on unused variables.
    fn unused_code(x: u32) -> u32 {
      let y: u32 = 3;
      let z: u32 = 4;
      x * y
    }

    // Unclear semantics in tuple assignments
    //
    // This code is probably legal, but it is not clear which value
    // will be assigned to i in the end.
    //

    // Note that types have to be defined globally (we should probably change that
    // and allow function-scoped types. Also, it is weird that 'type' needs a
    // semicolon.
    //
    type Tuple2 = (u32, u32);

    fn tuple_assign(x: u32, y: u32) -> (u32) {
      let (i, i): Tuple2 = (x, y);
      i
    }

    // Invalid init expression size
    //
    // It is not clear whether or not this should be caught in the front-end,
    // or whether some other semantic applies, eg., non-initialized variables
    // are auto-initialized to the zero element of a given type.
    fn invalid_init_size() {
      const K: u32[2] = [0x428a2f98];
      K[1]
    }

    // Invalid init expression type
    //
    // This should probably be an error
    fn invalid_init_type() {
      const K: u32[2] = ['a', 'b'];
      K[1]
    }

    // Unused parameter
    //
    // Parameter is not used, this could point to a code problem.
    //
    fn unused_parm(x: u32, y: u32) {
      x + 1
    }

    // Double defined parameter
    //
    // Two parameters have the same name. This could point to a code problem.
    //
    fn double_defined_parm(x: u32, x: u32) {
      x + 1
    }

    // Invalid index
    //
    // Static or dynamic indices can be out of range. The front-end could find
    // static bounds check violations, but only a limited subset. The middle-end
    // can find a wider class of violations via copy propatation. The run-time
    // can find dynamic violations. This test should help to clarify what the limits
    // are.
    //
    fn invalid_index(x: u32) -> u32 {
      let K: u32[10] = (0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
         let v: u32 = 10;
           K[10]  // static violation with constant. Should be an error.
           + K[v] // static violation. Should be found via copy prop.
           + K[x] // dynamic violation. Can only be found at runtime or via
                  // inter-procedural copy propagation.
    }

    // Tail recursion
    //
    // Tail recursion can be transformed into a loop, but unless we have
    // the corresponding pass, this type of code must be considered
    // illegal.
    //
    fn tail_recursion(x: u32) -> u32 {
       select(x==0, 0, x + tail_recursion(x - 1))
    }

    // Regular recursion
    //
    // Is also illegal.
    //
    fn regular_recursion(x: u32) -> u32 {
       let y: u32 = 7;
       let z: u32 = regular_recursion(x - 1);
       y + z
    }
    """
        with fakefs_test_util.scoped_fakefs(self.fake_filename, program):
            parser_helpers.parse_text(program,
                                      name=self.fake_filename,
                                      print_on_error=True,
                                      filename=self.fake_filename)