Open seanbaxter opened 2 months ago
If a template type parameter is specialized with a type that has lifetime binders, are template lifetime parameters created for that type argument and attached to the specialization?
For alias templates, I think the answer here is yes.
We want the canonical forms to compare equal.
So given:
#feature on safety
template<class T+>
struct box
{
T t;
};
template<class T+>
choice optional {
default none,
some(T);
};
template<class T+>
using unique_ptr = optional<box<T>>;
struct string_view/(a)
{
char* p_;
};
void f1/(a)(unique_ptr<string_view/a>) {}
void f2/(a)(optional<box<string_view/a>>) {}
static_assert(f1 == f2);
we should want unique_ptr
to become: optional<box<string_view/T0.0.0>>/a
which is in line with the canonicalized representation.
This would mean that similarly, passing a type with bound lifetime parameters as T
to an alias template would be ill-formed.
The invented lifetime template parameters are part of the class template specialization. That's a declaration. What are the invented lifetime template parameters of an alias template part of? They can't be part of the alias template specialization because the compilers needs to see through the canonical type, as it does with typedef. Which declaration owns those lifetimes?
We certainly shouldn't strip lifetime arguments, but I don't think we necessarily need to add placeholders with T+. What would thst achieve?
Wouldn't it make sense to just create the class template specialization if it doesn't already exist and then have the type alias "point" to that, for lack of a better term? The alias template shouldn't own anything, the invented lifetime parameters would belong to the class template's specialization.
I think what you're suggesting is that typename T on an alias template shouldn't do anything with lifetime arguments--it will just pass the template arguments through when specializing the type-id. This seems good, but it's different semantic behavior than passing typename T arguments to class templates or function templates.
I've been revising my opinions to always create lifetime template parameters for class template arguments with lifetime binders. No T+
needed. The lifetime arguments are stripped during specialization.
That means:
std::is_same<int^, int^>::value;
// Implicitly add placeholders to unbound binders in class template arguments.
// The above transforms to:
std::is_same<int^/_, int^_>::value;
// During normalization, replace lifetime arguments with invented
// template lifetime parameters.
// If the complete template was chosen (but it won't be), you'd get:
std::is_same<int^/#T0, int^/#T1>/_/_::value
// During specialization, strip lifetimes when evaluating partial and
// explicit specializations.
// The above matches:
template<typename T>
std::is_same<T, T>;
// The partial specialization has one template lifetime parameter:
template<typename T>
class is_same/(#T0)<T/#T0, T/#T0>;
// That gets hoisted into the nested-name-specifier during
// value lookup:
std::is_same<int^, int^>/_::value;
// value is a bool, so it doesn't require transfer of the
// lifetime arguments from nested-name-specifier to the
// member name.
// But a member type would!
typename std::foo<int^, int^>::type/_;
I think the mechanical transfer of lifetime arguments from nested-name to member name should be reliable.
As far as the partial or explicit specialization having a different constraint graph than the primary template, that's probably a good thing, as long as the MIR has a complete type for the purpose of type relation.
If a template type parameter is specialized with a type that has lifetime binders, are template lifetime parameters created for that type argument and attached to the specialization?
For class templates,
typename T+
indicates a type parameter than binds lifetime arguments. If a template argument doesn't have lifetime arguments, it is provided is implicit placeholder arguments. It is part of the design that passing a template argument with any bound lifetimes to atypename T
parameter on a class template is ill-formed. This way,std::is_same<int^, int^>::value
evaluates true, because the template arguments have unbound lifetimes, is_same's type parameter istypename T
, so no template lifetime parameters are created and the partial specialization is found.But what about all the other kinds of template entities?