nickg / nvc

VHDL compiler and simulator
https://www.nickg.me.uk/nvc/
GNU General Public License v3.0
589 stars 75 forks source link

Function overloading not working as expected #875

Closed sudden6 closed 2 months ago

sudden6 commented 3 months ago

The following minimal code:

package repro is
  subtype instruction32_t is bit_vector(31 downto 0);
  subtype opcode32_t is bit_vector(6 downto 0);

  function has_rd(op  : opcode32_t) return boolean;
  function has_rd(ins : instruction32_t) return boolean;
end;

package body repro is
  function has_rd(op : opcode32_t) return boolean is
  begin
    return false;
  end function;

  function has_rd(ins : instruction32_t) return boolean is
  begin
    return true;
  end function;
end package body;

Leads to this error, even though I believe it's correct VHDL-2008, because the functions differ in their subtypes:

nvc -a repro/overload.vhd 
** Error: HAS_RD [INSTRUCTION32_T return BOOLEAN] already declared in this region
   > repro/overload.vhd:6
   |
 5 |   function has_rd(op  : opcode32_t) return boolean;
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ previous declaration was here
 6 |   function has_rd(ins : instruction32_t) return boolean;
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate declaration
** Error: design unit depends on WORK.REPRO which was analysed with errors
   > repro/overload.vhd:9
   |
 9 | package body repro is
   | ^^^^^^^^^^^^^^^^^^

Version is current git:

nvc --version
nvc 1.12-devel (1.11.0.r182.gaea6ec4a) (Using LLVM 18.1.2)
Copyright (C) 2011-2024  Nick Gasson
This program comes with ABSOLUTELY NO WARRANTY. This is free software, and
you are welcome to redistribute it under certain conditions. See the GNU
General Public Licence for details.
nickg commented 2 months ago

Subtypes are not distinct for the purposes of overload resolution. See section 4.5.1 of the 2008 LRM:

Two formal parameter lists are said to have the same parameter type profile if and only if they have the same number of parameters, and if at each parameter position the corresponding parameters have the same base type.

The error above then follows from the visibility rules in section 12.3:

Each of two declarations is said to be a homograph of the other if and only if both declarations have the same designator, and they denote different named entities, and either overloading is allowed for at most one of the two, or overloading is allowed for both declarations and they have the same parameter and result type profile (see 4.5.1).

So the two functions above are homographs since they have the same designator (has_rd), the parameter base types are the same (bit_vector), and the return type is the same. Then:

Two declarations that occur immediately within the same declarative region, [..], shall not be homographs, unless exactly one of them is the implicit declaration of a predefined operation or is an implicit alias of such an implicit declaration.

The error message could probably be improved a bit though.

For your example to work you need to make instruction32_t and opcode32_t separate types rather than subtypes. Something like:

  type instruction32_t is array (31 downto 0) of bit;
  type opcode32_t is array (6 downto 0) of bit;

  function has_rd(op  : opcode32_t) return boolean;
  function has_rd(ins : instruction32_t) return boolean;

FWIW ModelSim gives a very similar error:

** Error: test.vhd(6): (vcom-1295) Function "has_rd" has already been defined in this region.
** =====> Prior declaration of "has_rd" is at test.vhd(5).
** Note: test.vhd(7): VHDL Compiler exiting
nickg commented 2 months ago

It now reports this:

** Error: homograph of HAS_RD [INSTRUCTION32_T return BOOLEAN] already declared in this region
   > test.vhd:6
   |
 5 |   function has_rd(op  : opcode32_t) return boolean;
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ previous declaration was here
 6 |   function has_rd(ins : instruction32_t) return boolean;
   |   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ duplicate declaration
   |
   = Note: only the base type is considered when determining if two overloads have the same 
           parameter type profile