Overload resolution: type conversions vs. functions

Is your capability/feature request related to a problem?

Why isn't overload resolution for VHDL's type conversions (what other languages call type casting) the same as for a function - or at least require that a type conversion only be used with an expression that is closely related type.

Consider the following (borrowed from https://stackoverflow.com/questions/79573800/vhdl-cant-determine-type-of-object/79574123#79574123):

In the following, the type conversion is ambiguous:

library IEEE ;
use ieee.std_logic_1164.all ; 
use ieee.numeric_std.all ; 

entity concat is 
end entity concat ; 
architecture test of concat is 
  type slvn_array is array (natural range <>) of  std_logic_vector ;
  constant ENDPOINT0_CTRL_REG        :   std_logic_vector(12 downto 0)  :=   '0' & x"040";
  signal usb_hid_phy_offload_address :   std_logic_vector(12 downto 0);
  signal cfg_hid_ep                  :   slvn_array(2 downto 0)(7 downto 0)  ;

begin
  TestProc : process

  begin
    usb_hid_phy_offload_address <= 
        std_logic_vector(unsigned(ENDPOINT0_CTRL_REG) + 
        unsigned((("00000") & (cfg_hid_ep(1)(5 downto 0)) & ("00"))));
    wait ; 

  end process TestProc ; 
end architecture test ; 

It is ambiguous because of VHDL implicitly defines '&' for all single dimensional arrays. Hence concatenate is defined for (thanks to @nickg1 for the great error messages in nvc that I can borrow from).

  "&" [STD_LOGIC_VECTOR, STD_LOGIC_VECTOR return SLVN_ARRAY]
  "&" [STD_ULOGIC_VECTOR, STD_ULOGIC_VECTOR return STD_ULOGIC_VECTOR] 

OTOH, if I add a function to do the same thing, it is legal because the overload resolution of the function is considered, where as, for type conversions there is no elimination of solutions that are not closely related.

library IEEE ;
use ieee.std_logic_1164.all ; 
use ieee.numeric_std.all ; 

entity concat is 
end entity concat ; 
architecture test of concat is 
  . . .

  function to_unsigned(A : std_logic_vector) return unsigned is 
  begin
    return unsigned(A) ;
  end function to_unsigned ; 

begin
  TestProc : process

  begin
    usb_hid_phy_offload_address <= 
        std_logic_vector(unsigned(ENDPOINT0_CTRL_REG) + 
        to_unsigned((("00000") & (cfg_hid_ep(1)(5 downto 0)) & ("00"))));
      
    wait ; 

  end process TestProc ; 
end architecture test ; 

In VHDL-2019 subprogram definition terms, a type conversion for unsigned has the following function declaration, hence, it is confusing that these do not currently work the same.

function unsigned ( type is array (private range <>) of std_ulogic) return unsigned ;

Historical artifacts for this issue

ISAC issue discussed this issue and made updates to 1076-2002 regarding this issue to clarify the LRM, but did not address the user issue - as they asked for a interpretation and not an enhancement to the language: https://www.eda-twiki.org/isac/IRs-VHDL-2002/IR2126.txt

Kudos to https://stackoverflow.com/users/16145658/user16145658 for providing the link to the ISAC discussion and for sparing on SO by asking so many insightful questions.

Describe the solution you'd like

I would like type conversions to require that the expression be closely related to the target type.

It should be noted that case statements (from 10.9) require it to be a complete context with conditions:

This type shall be determined by applying the rules of 12.5 to the expression considered as a complete context, using the rule that the expression shall be of a discrete type or a one-dimensional character array type.

Currently 1076-2019, 9.3.6, Type conversions, states:

The target type of a type conversion is the base type of the type mark, and the target subtype of a type conversion is the type or subtype denoted by the type mark. The type of the operand of a type conversion shall be determined by applying the rules of 12.5 to the operand considered as a complete context. (In particular, the type of the operand shall be determinable independent of the target type). Furthermore, . . .

I suggest that we modify this to state:

The target type of a type conversion is the base type of the type mark, and the target subtype of a type conversion is the type or subtype denoted by the type mark. The type of the operand of a type conversion shall be determined by applying the rules of 12.5 to the operand considered as a complete context, using the rule that the expression shall be closely related to the target type. (In particular, the type of the operand shall be determinable independent of the target type). Furthermore, . . .

Note the text in parentheses above that is deleted is a repeat of the rules of 12.5, so repeat it here simply makes the LRM more complicated to update.

Describe alternatives you've considered

Additional context

Edited by JimLewis
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information