Issue 18216: Dangerous implicit conversions (vsiplxx-rtf) Source: (Mr. Brooks Moses, brooks.moses(at)dpdx.net) Nature: Revision Severity: Minor Summary: This issue is copied from an internally-filed issue at Mentor that dates back to the early days of the specification. Since that time, some usage patterns have emerged that appear useful -- most notably, the implicit use of a length_type when a Domain<1> is required. This particular case should either be preserved or additional overloads of the relevant functions should be added. However, in general this complaint is still quite relevant -- for instance, we should not be implicitly converting a length_type value to a LU_solver object. Note that I have not edited this to account for any changes to the specification (either section numbers or normative changes) since the issue was initially submitted in 2005. In most cases, the resolution -- adding an "explicit" keyword to many of these constructors -- seems straightforward and obvious. Chances are very high that any existing code which is broken thereby was erroneous in the first place. ---- Many of the constructors specified can take a single argument but are not declared "explicit". A complete list: Domain<1>(length_type) Index<1>(index_type) Dense<D>(Domain<D> const&) const_Vector(Block&) const_Vector(Vector const&) * const_Vector<>(Vector const&) Vector<>(Block&) template <typename T0, typename Block0> Vector(Vector<T0, Block0> const&) const_Matrix<>(Block&) * const_Matrix<>(Matrix const&) Matrix(Block&) template <typename T0, typename Block0> Matrix(Matrix<T0, Block0> const&) const_Tensor<>(Block&) * const_Tensor<>(Tensor const&) Tensor(Block&) template <typename T0, typename Block0> Tensor(Tensor<T0, Block0> const&) lud<>(length_type) [or LU_solver<>(length_type), see #29] Rand<>(index_type) Of these, only the starred (*) conversions are defensible, and even they will require some design adjustments. All the rest are a problem; most are fixable simply by adding "explicit". For the case of Domain<1>, this will require redesign of other components to restore convenient usage; perhaps similarly for some of the conversions from Block&. Also, in the requirements list 6.3, the line View(Block &) should be annotated to indicate that the conversion should not be implicit. To expand on why these implicit conversions are dangerous... First, implicit conversions from native numeric types are especially problematic because of the promiscuous conversions among those types, inherited from C, that occur even before calling the constructor. As a result, any numeric type, whether int, float, char, or bool, matches the constructor argument, and thus is accepted freely in place of the class type by any function that takes an argument of that type, regardless of whether it makes sense. These mistakes can be very hard to spot; sometimes it results from calling f(a,b) when f(b,a) was meant. (Functions declared to take numeric types can sometimes be made safer by overloading with another that takes a different, but acceptable, numeric type.) Second, implicit conversions of any kind provoke annoying compiler error messages complaining of ambiguities when more than one conversion path is possible. The more implicit conversions that are possible, the more frequently such ambiguities occur. The workaround for such an ambiguity is to add a cast to the exact type, but casts are themselves a source of errors, and besides obscuring the logic of the code, they eliminate the convenience the implicit conversion was supposed to provide. Third, in cases where one conversion path is favored by the compiler over another, the compiler may silently choose what the programmer would consider the wrong one. Fourth, template argument matching ignores conversions, making those that do work less conveniently useful than they may seem. Resolution: In the interest of closing the current RTF so that the solutions to resolved issues can be made publically available, this issue is deferred to the next RTF. Disposition: Deferred Revised Text: Actions taken: October 23, 2012: received issue Discussion: End of Annotations:===== te: Tue, 23 Oct 2012 13:06:31 -0700 From: Brooks Moses User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64; rv:10.0.2) Gecko/20120216 Thunderbird/10.0.2 To: Subject: VSIPL++ issue (#19 from me) X-OriginalArrivalTime: 23 Oct 2012 20:06:31.0570 (UTC) FILETIME=[E4FE7720:01CDB159] Name: Brooks Moses Employer: Mentor Graphics mailFrom: brooks_moses@mentor.com Terms_Agreement: I agree Specification: VSIPL++ Section: (various) FormalNumber: ptc/2012-07-27 Version: 1.2 - FTF Beta 1 Doc_Year: 2012 Doc_Month: August Doc_Day: 10 Page: (various) Title: Dangerous implicit conversions Nature: Bug Severity: Minor B1: Report Issue Description: This issue is copied from an internally-filed issue at Mentor that dates back to the early days of the specification. Since that time, some usage patterns have emerged that appear useful -- most notably, the implicit use of a length_type when a Domain<1> is required. This particular case should either be preserved or additional overloads of the relevant functions should be added. However, in general this complaint is still quite relevant -- for instance, we should not be implicitly converting a length_type value to a LU_solver object. Note that I have not edited this to account for any changes to the specification (either section numbers or normative changes) since the issue was initially submitted in 2005. In most cases, the resolution -- adding an "explicit" keyword to many of these constructors -- seems straightforward and obvious. Chances are very high that any existing code which is broken thereby was erroneous in the first place. ---- Many of the constructors specified can take a single argument but are not declared "explicit". A complete list: Domain<1>(length_type) Index<1>(index_type) Dense(Domain const&) const_Vector(Block&) const_Vector(Vector const&) * const_Vector<>(Vector const&) Vector<>(Block&) template Vector(Vector const&) const_Matrix<>(Block&) * const_Matrix<>(Matrix const&) Matrix(Block&) template Matrix(Matrix const&) const_Tensor<>(Block&) * const_Tensor<>(Tensor const&) Tensor(Block&) template Tensor(Tensor const&) lud<>(length_type) [or LU_solver<>(length_type), see #29] Rand<>(index_type) Of these, only the starred (*) conversions are defensible, and even they will require some design adjustments. All the rest are a problem; most are fixable simply by adding "explicit". For the case of Domain<1>, this will require redesign of other components to restore convenient usage; perhaps similarly for some of the conversions from Block&. Also, in the requirements list 6.3, the line View(Block &) should be annotated to indicate that the conversion should not be implicit. To expand on why these implicit conversions are dangerous... First, implicit conversions from native numeric types are especially problematic because of the promiscuous conversions among those types, inherited from C, that occur even before calling the constructor. As a result, any numeric type, whether int, float, char, or bool, matches the constructor argument, and thus is accepted freely in place of the class type by any function that takes an argument of that type, regardless of whether it makes sense. These mistakes can be very hard to spot; sometimes it results from calling f(a,b) when f(b,a) was meant. (Functions declared to take numeric types can sometimes be made safer by overloading with another that takes a different, but acceptable, numeric type.) Second, implicit conversions of any kind provoke annoying compiler error messages complaining of ambiguities when more than one conversion path is possible. The more implicit conversions that are possible, the more frequently such ambiguities occur. The workaround for such an ambiguity is to add a cast to the exact type, but casts are themselves a source of errors, and besides obscuring the logic of the code, they eliminate the convenience the implicit conversion was supposed to provide. Third, in cases where one conversion path is favored by the compiler over another, the compiler may silently choose what the programmer would consider the wrong one. Fourth, template argument matching ignores conversions, making those that do work less conveniently useful than they may seem.