c++: Fix deduction from auto template parameter [PR93083]

The check in do_class_deduction to handle passing one class placeholder
template parm as an argument for itself needed to be extended to also handle
equivalent parms from other templates.

gcc/cp/ChangeLog:

	PR c++/93083
	* pt.c (convert_template_argument): Handle equivalent placeholders.
	(do_class_deduction): Look through EXPR_PACK_EXPANSION, too.

gcc/testsuite/ChangeLog:

	PR c++/93083
	* g++.dg/cpp2a/nontype-class40.C: New test.
This commit is contained in:
Jason Merrill 2020-11-25 17:05:24 -05:00
parent df933e307b
commit a95753214b
2 changed files with 86 additions and 5 deletions

View file

@ -8266,7 +8266,7 @@ convert_template_argument (tree parm,
/* When determining whether an argument pack expansion is a template,
look at the pattern. */
if (TREE_CODE (arg) == TYPE_PACK_EXPANSION)
if (PACK_EXPANSION_P (arg))
arg = PACK_EXPANSION_PATTERN (arg);
/* Deal with an injected-class-name used as a template template arg. */
@ -29013,6 +29013,12 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
if (DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))
return ptype;
/* Initializing one placeholder from another. */
if (init && TREE_CODE (init) == TEMPLATE_PARM_INDEX
&& is_auto (TREE_TYPE (init))
&& CLASS_PLACEHOLDER_TEMPLATE (TREE_TYPE (init)) == tmpl)
return cp_build_qualified_type (TREE_TYPE (init), cp_type_quals (ptype));
/* Look through alias templates that just rename another template. */
tmpl = get_underlying_template (tmpl);
if (!ctad_template_p (tmpl))
@ -29029,10 +29035,6 @@ do_class_deduction (tree ptype, tree tmpl, tree init,
"with %<-std=c++20%> or %<-std=gnu++20%>");
}
if (init && TREE_TYPE (init) == ptype)
/* Using the template parm as its own argument. */
return ptype;
tree type = TREE_TYPE (tmpl);
bool try_list_ctor = false;

View file

@ -0,0 +1,79 @@
// PR c++/93083
// { dg-do compile { target c++20 } }
template<unsigned N>
struct FixedString
{
char buf[N + 1]{};
constexpr FixedString(char const* s) {
for (unsigned i = 0; i != N; ++i) buf[i] = s[i];
}
auto operator<=>(const FixedString&) const = default;
constexpr operator char const*() const { return buf; }
constexpr static unsigned size() noexcept { return N; }
};
template<unsigned N> FixedString(char const (&)[N]) -> FixedString<N - 1>;
template <FixedString... names>
struct name_list
{
template <FixedString name>
using add_name = name_list<
names...,
FixedString<name.size()>{ name }
>;
};
int main()
{
using names =
name_list<>
::add_name<"Zaphod Beeblebrox">;
}
// ----------------
template <int N> struct literal {
constexpr literal(const char (&input)[N]) noexcept { }
constexpr literal(const literal &) noexcept { }
};
template <literal Name, int id> struct field { };
template <literal Name> struct field<Name, 1u> { };
// ----------------
template <int N>
struct use_as_nttp {};
template <use_as_nttp Value>
struct has_nttp {};
template <use_as_nttp Value>
using has_nttp_2 = has_nttp<Value>;
// ----------------
using size_t = decltype(sizeof(0));
template <size_t N>
struct string_literal
{
constexpr string_literal(const char*) {}
string_literal(string_literal const&) = default;
};
template <size_t N>
string_literal(const char (&)[N]) -> string_literal<N - 1>;
template <string_literal Str>
struct type_string { };
template <string_literal Str>
void foo() {
type_string<Str>{};
}