LLVM Bugzilla is read-only and represents the historical archive of all LLVM issues filled before November 26, 2021. Use github to submit LLVM bugs

Bug 30589 - std::complex with a custom type does not work because of how std::__promote is defined
Summary: std::complex with a custom type does not work because of how std::__promote i...
Status: RESOLVED INVALID
Alias: None
Product: libc++
Classification: Unclassified
Component: All Bugs (show other bugs)
Version: unspecified
Hardware: PC Linux
: P normal
Assignee: Unassigned Clang Bugs
URL:
Keywords:
Depends on:
Blocks: 19189
  Show dependency tree
 
Reported: 2016-10-01 15:21 PDT by Hal Finkel
Modified: 2019-06-03 18:48 PDT (History)
3 users (show)

See Also:
Fixed By Commit(s):


Attachments
test case (7.05 KB, text/x-c++src)
2016-10-01 15:21 PDT, Hal Finkel
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Hal Finkel 2016-10-01 15:21:03 PDT
Created attachment 17394 [details]
test case

In https://reviews.llvm.org/D18639, Eric asked if we supporting using std::complex with custom numeric types. The answer is: mostly. I've attached the test case I started writing. This compiles with libstdc++, and in fact, requires a lot less of the custom type than we do (e.g. we require fabs in addition to abs, scalbn, logb, and many other functions). With libc++, however, we do it one compilation error which looks like a problem with libc++ itself:

include/c++/v1/complex:1321:39: error: no matching function for call to 'pow'
    complex<_Tp> __z = log(__x + sqrt(pow(__x, _Tp(2)) - _Tp(1)));
...
include/c++/v1/complex:1109:1: note: candidate template ignored: substitution failure [with _Tp = ct::va
lue, _Up = ct::value]: no type named 'type' in 'std::__1::__promote<ct::value, ct::value, void>'
pow(const complex<_Tp>& __x, const _Up& __y)

The problem here seems to be that std::__promote, which is defined in type_traits, only works for types for which __numeric_type<T>::type exists, and that is only for builtin numeric types. As a result, this template does not match:

template<class _Tp, class _Up>
inline _LIBCPP_INLINE_VISIBILITY
typename enable_if
<
    is_arithmetic<_Up>::value,
    complex<typename __promote<_Tp, _Up>::type>
>::type
pow(const complex<_Tp>& __x, const _Up& __y)
{
    typedef complex<typename __promote<_Tp, _Up>::type> result_type;
    return _VSTD::pow(result_type(__x), result_type(__y));
}
Comment 1 Hal Finkel 2016-10-01 15:23:30 PDT
This also brings up another issue: Does the standard say anything about what is required of a type T for std::complex<T> to work (i.e. for the various functions defined in <complex> to do something)?
Comment 2 Marshall Clow (home) 2016-10-01 20:44:44 PDT
> This also brings up another issue: Does the standard say anything about what is required of a type T for std::complex<T> to work.

[complex.numbers]/2 says:
The effect of instantiating the template complex for any type other than float, double, or long double is unspecified. The specializations complex<float>, complex<double>, and complex<long double> are literal types (3.9).
Comment 3 Hal Finkel 2016-10-01 21:26:19 PDT
(In reply to comment #2)
> > This also brings up another issue: Does the standard say anything about what is required of a type T for std::complex<T> to work.
> 
> [complex.numbers]/2 says:
> The effect of instantiating the template complex for any type other than
> float, double, or long double is unspecified. The specializations
> complex<float>, complex<double>, and complex<long double> are literal types
> (3.9).

What do you think we should do; there seem to be many options:

 1. Do nothing.
 2. Force the instantiation to fail for other types. Add a test for that.
 3. Have the compiler issue a warning if we instantiate with other types (but otherwise do nothing).
 4. Add a test for instantiation using a custom type, but test only basic operations (saying we support that as an extension, but not the transcendental functions).
 5. Fix things so everything, including the transcendental functions, work on custom types, and then add a test covering everything.

Do you happen to know if there's any history here of the committee discussing specifically allowing other types?
Comment 4 Marshall Clow (home) 2016-10-02 00:53:56 PDT
> Do you happen to know if there's any history here of the committee discussing specifically allowing other types?


I'm sure there was such a discussion; otherwise the paragraph that I quoted would not be there in the standard.

However, the first sentence of that paragraph has been unchanged in the standard since 1998; so any such discussion was a long time ago.
Comment 5 Hal Finkel 2016-10-02 23:34:59 PDT
(In reply to comment #4)
> > Do you happen to know if there's any history here of the committee discussing specifically allowing other types?
> 
> 
> I'm sure there was such a discussion; otherwise the paragraph that I quoted
> would not be there in the standard.
> 
> However, the first sentence of that paragraph has been unchanged in the
> standard since 1998; so any such discussion was a long time ago.

Indeed ;)

In any case, any opinion on how to move forward. All things considered, I think that my option 4 is probably the best, and I'd like to propose updating the standard to allow the same. There are good use cases for this, and I expect that all, or almost all, implementations already work in this regard.

FWIW, boost_1_62_0/libs/numeric/ublas/benchmarks/bench4/bench4.cpp has:

   74 #ifdef USE_BOOST_COMPLEX
   75 
   76 template struct peak<boost::complex<boost::numeric::interval<float> > >;
   77 template struct peak<boost::complex<boost::numeric::interval<double> > >;
   78 
   79 #endif

which does represent a good use case.
Comment 6 Marshall Clow (home) 2017-05-17 21:08:09 PDT
I'm leaning towards option #2 - basically, static_assert.

I think that's better than "sorta working" or "working for some things and not others".

The other choice would be your option #5 - make everything work - and for that I would be happy to review patches.
Comment 7 Marshall Clow (home) 2019-06-03 18:48:00 PDT
I'm going to close this bug.
We're compliant here - the results are "unspecified".

If we want to add support for custom types to our complex implementation, that's fine. But this bug isn't going to help is.