def test_unknown_fortran_type(): ''' Check the constructor and 'declaration' property of the UnknownFortranType class. ''' with pytest.raises(TypeError) as err: UnknownFortranType(1) assert ("constructor expects the original variable declaration as a " "string but got an argument of type 'int'" in str(err.value)) decl = "type(some_type) :: var" utype = UnknownFortranType(decl) assert str(utype) == "UnknownFortranType('" + decl + "')" assert utype.declaration == decl
def test_name_clash_derived_type_def(f2008_parser): ''' Check that the frontend raises an error if it encounters a definition of a derived type with a name that clashes with another symbol. ''' fake_parent = KernelSchedule("dummy_schedule") symtab = fake_parent.symbol_table # Add a RoutineSymbol to the symbol table that will clash with the name # of the derived type. symtab.add(RoutineSymbol("my_type")) # Add a DataTypeSymbol to the symbol table. Make it appear that we've # already seen a definition of this symbol by making it of # UnknownFortranType. symtab.new_symbol("my_type2", symbol_type=DataTypeSymbol, datatype=UnknownFortranType("huh")) processor = Fparser2Reader() fparser2spec = f2008_parser( FortranStringReader("subroutine my_sub()\n" " type :: my_type\n" " integer :: flag\n" " end type my_type\n" "end subroutine my_sub\n")) # This should raise an error because the Container symbol table will # already contain a RoutineSymbol named 'my_type' with pytest.raises(SymbolError) as err: processor.process_declarations(fake_parent, fparser2spec.content, []) assert ("Error processing definition of derived type 'my_type'. The " "symbol table already contains an entry with this name but it is a" " 'RoutineSymbol' when it should be a 'DataTypeSymbol' (for the " "derived-type definition 'TYPE :: my_type\n INTEGER :: flag\n" "END TYPE my_type')" in str(err.value)) # Repeat but with a derived-type name that will clash with our existing # DataTypeSymbol fparser2spec = f2008_parser( FortranStringReader("subroutine my_sub2()\n" " type :: my_type2\n" " integer :: flag\n" " end type my_type2\n" "end subroutine my_sub2\n")) with pytest.raises(SymbolError) as err: processor.process_declarations(fake_parent, fparser2spec.content, []) assert ("Error processing definition of derived type 'my_type2'. The " "symbol table already contains a DataTypeSymbol with this name but" " it is of type 'UnknownFortranType' when it should be of " "'DeferredType'" in str(err.value))
def create(cls, children, symbol_table, ast=None, options=None): ''' Creates a new (sub-class of a) PSyData node with the supplied 'children' nodes as its children. The symbols used by the PSyData API are added to the supplied symbol table. This is a class method so that it acts as a factory for the various sub-classes of PSyDataNode. :param children: the PSyIR nodes that will become children of the \ new PSyData node. :type children: list of :py:class:`psyclone.psyir.nodes.Node` :param symbol_table: the associated SymbolTable to which symbols \ must be added. :type symbol_table: :py:class:`psyclone.psyir.symbols.SymbolTable` :parent ast: reference to fparser2 parse tree for the routine being \ instrumented with PSyData calls. :type ast: :py:class:`fparser.two.Fortran2003.Base` :param options: a dictionary with options for transformations. :type options: dictionary of string:values or None :param str options[prefix"]: a prefix to use for the PSyData module \ name (``prefix_psy_data_mod``) and the PSyDataType \ (``prefix_PSyDataType``) - a "_" will be added automatically. \ It defaults to "", which means the module name used will just be \ ``psy_data_mod``, and the data type ``PSyDataType``. :param (str,str) options["region_name"]: an optional name to use for \ this PSyDataNode, provided as a 2-tuple containing a module name \ followed by a local name. The pair of strings should uniquely \ identify a region unless aggregate information is required \ (and is supported by the runtime library). :raises TypeError: if the supplied children or symbol table are not \ of the correct type. ''' if not isinstance(children, list): raise TypeError("Error in PSyDataNode.create(). The 'children' " "argument must be a list (of PSyIR nodes) but got " "'{0}'".format(type(children).__name__)) if children and not all(isinstance(child, Node) for child in children): raise TypeError( "Error in PSyDataNode.create(). The 'children' argument must " "be a list of PSyIR nodes but it contains: {0}".format( [type(child).__name__ for child in children])) if not isinstance(symbol_table, SymbolTable): raise TypeError( "Error in PSyDataNode.create(). The 'symbol_table' argument " "must be an instance of psyir.symbols.SymbolTable but got " "'{0}'.".format(type(symbol_table).__name__)) data_node = cls(ast=ast, options=options) # Ensure that we have a container symbol for the API access try: csym = symbol_table.lookup_with_tag(data_node.fortran_module) except KeyError: # The tag doesn't exist which means that we haven't already added # this Container as part of a PSyData transformation. csym = ContainerSymbol(data_node.fortran_module) symbol_table.add(csym, tag=data_node.fortran_module) # A PSyData node always contains a Schedule # TODO 435 we can probably get rid of _insert_schedule() once we # do away with the ast argument? # pylint: disable=protected-access sched = data_node._insert_schedule(children=children, ast=ast) data_node.addchild(sched) # The use statement that will be inserted by the update() method. Any # use of a module of the same name that doesn't match this will result # in a NotImplementedError at code-generation time. # TODO #435 remove this when removing the update() method data_node.use_stmt = "use {0}, only: ".format( data_node.fortran_module) + ", ".join(symbol.name for symbol in data_node.imported_symbols) # Add the symbols that will be imported from the module. Use the # PSyData names as tags to ensure we don't attempt to add them more # than once if multiple transformations are applied. for sym in data_node.imported_symbols: symbol_table.symbol_from_tag(sym.name, symbol_type=sym.symbol_type, interface=GlobalInterface(csym), datatype=DeferredType()) # Store the name of the PSyData variable that is used for this # PSyDataNode. This allows the variable name to be shown in str # (and also, calling create_name in gen() would result in the name # being changed every time gen() is called). data_node._var_name = symbol_table.next_available_name( data_node._psy_data_symbol_with_prefix) psydata_type = UnknownFortranType( "type({0}), save, target :: {1}".format(data_node.type_name, data_node._var_name)) symbol_table.new_symbol(data_node._var_name, symbol_type=DataSymbol, datatype=psydata_type) return data_node