This is an archive of the discontinued LLVM Phabricator instance.

[libc++] Fix PR 31938 - std::basic_string constructors use non-deductible parameter types.
ClosedPublic

Authored by EricWF on Feb 10 2017, 7:55 PM.

Details

Summary

This patch fixes http://llvm.org/PR31938. The description below is copy/pasted from the bug:

The standard says:

template<class charT, class traits = char_traits<charT>,

class Allocator = allocator<charT>>

class basic_string {

using value_type = typename traits::char_type;
// ...
basic_string(const charT* s, const Allocator& a = Allocator());

};

libc++ actually chooses to declare the constructor as

basic_string(const value_type* s, const Allocator& a = Allocator());

The implicit deduction guides from class template argument deduction make what was previously an implementation detail visible:

std::basic_string s = "foo"; // error, can't deduce charT.

The constructor in question is in the libc++ DSO, but fortunately it looks like fixing this will not result in an ABI break.

@rsmith How does this look? I did more than just the constructors mentioned in the PR, but IDK how far to take it.

Diff Detail

Event Timeline

EricWF created this revision.Feb 10 2017, 7:55 PM
rsmith edited edge metadata.Feb 10 2017, 8:49 PM

Looks good, though there are some value_type constructors left. I've not checked the standard to see if they are all declared with charT.

include/string
782

Did you skip this one intentionally?

788

Likewise these two.

812

And these

EricWF added inline comments.Feb 11 2017, 2:47 AM
include/string
782

Yes. size_type is a typedef for allocator_traits<Allocator>::size_type, This causes the basic_string(CharT*, Allocator const&) to always be chosen instead, resulting in a incorrect allocator type.

788

I thought they would suffer the same fate as the above overload but that is not the case. I'll update after reviewing each constructor.

812

Just making sure I was on the right track first.

EricWF updated this revision to Diff 88088.Feb 11 2017, 2:50 AM
  • Fix the initializer list constructors.
  • Add tests for almost every constructor.

The following two examples still do not work:

std::basic_string s1("hello world", 2); // deduces Allocator = int
std::basic_string s2("hello world", 0, 2); // deduces Allocator = int

This is because the Allocator argument is not normally deduced, and
therefore would not normally accept an integer argument.

rsmith added inline comments.Feb 12 2017, 1:02 PM
include/string
782

I don't think it will always be chosen instead; if the argument is of type size_t, the (const CharT*, size_type) overload should be chosen.

EricWF added inline comments.Feb 13 2017, 3:50 PM
include/string
782

OK, so it's not that it should always be taken. Instead I think any attempt to form the implicit deduction guide overloads for such a call will end up attempting to evaluate std::basic_string<char, char_traits<char>, unsigned long long> which is an ill-formed instantiation of basic_string.

So if building the overload set were to succeed then yes, the (const CharT*, size_type) would be callable with an argument of size_type. However it seems that the overload set is poisoned by (const CharT*, Allocator)

EricWF updated this revision to Diff 88803.Feb 16 2017, 4:33 PM
  • Enumerate and test each constructor.

Other than (5), all the failing cases look like they should fail per the current basic_string spec.

test/std/strings/basic.string/string.cons/implicit_deduction_guides.pass.cpp
58

I think that at least matches the standard as-is. I'm not sure this case is worth adding an explicit deduction guide for. *shrug*

108

Do you know why not?

292

I think the inability to deduce using this overload matches the standard. I don't think there's any way in general to map the type T to a charT.

EricWF added inline comments.Feb 16 2017, 5:21 PM
test/std/strings/basic.string/string.cons/implicit_deduction_guides.pass.cpp
58

I agree this probably matches the standard as-is, and that it's probably not worth adding an explicit guide for.
In general I don't think libc++ should add *any* explicit guides until the are required by the standard.

I'll remove the FIXME and clarify the comment before committing.

108

Yeah. It seems to choose the basic_string(size_type, CharT, const Allocator&) overload after deducing CharT and Allocator to be unsigned long.

The compiler diagnostics produced are:

/home/eric/workspace/libcxx/include/memory:1494:22: error: type 'allocator_type' (aka 'unsigned long') cannot be used prior to '::' because it has no members
    typedef typename allocator_type::value_type value_type;
                     ^
/home/eric/workspace/libcxx/include/string:788:28: note: in instantiation of template class 'std::__1::allocator_traits<unsigned long>' requested here
    basic_string(size_type __n, _CharT __c, const _Allocator& __a);
                           ^
/home/eric/workspace/libcxx/test/std/strings/basic.string/string.cons/implicit_deduction_guides.pass.cpp:111:23: note: while substituting deduced template arguments into function template '<deduction guide for basic_string>' [with _CharT = unsigned long, _Traits = (no value), _Allocator = unsigned long]
    std::basic_string s(sin, (size_t)1, (size_t)3);
292

OK. I'll update the comment before committing.

EricWF updated this revision to Diff 88812.Feb 16 2017, 5:25 PM
  • Clarify comments about conforming constructors that still don't support guides.
rsmith accepted this revision.Feb 16 2017, 5:26 PM
This revision is now accepted and ready to land.Feb 16 2017, 5:26 PM
EricWF closed this revision.Feb 16 2017, 5:28 PM