atomgalaxy / isocpp-universal-template-param

We propose a way to spell a universal template parameter kind. This would allow for a generic apply and other higher-order template metafunctions, and certain typetraits.
https://atomgalaxy.github.io/isocpp-universal-template-param/d1985r0.pdf
2 stars 2 forks source link

Ask Sean Baxter about why he implemented "Eager" checking #12

Closed BengtGustafsson closed 1 year ago

BengtGustafsson commented 1 year ago

and if he sees problems with implementing late checking.

mpusz commented 1 year ago

Maybe the only reason is the fact that:

This paper leans towards eager checking

atomgalaxy commented 1 year ago

asked, waiting for response.

atomgalaxy commented 1 year ago

Had a nice conversation with Sean.

He says late checking is more work and in his opinion doesn't buy enough. He also said https://godbolt.org/z/EPncdhar6 is impossible to do without the "eager" approach.

He recommends we remove all late-checking language from the paper. I agree.

BengtGustafsson commented 1 year ago

I'm stumped. The godbolt example does use late checking, albeit spelled differently than regular dependent name disambiguation, for some reason. That's according to my understanding at least: I thought eager checking meant that the only way to use a UTP was to forward it to another UTP parameter or to create specializations that would by definition be more specialized preventing the UTP base case from ever being used.

Going back to the proposal I see that the godbolt example uses the UTP in other places than template-parameter-list and template-argument-list, namely in the T.to_type and T.to_non_type expressions (?) which are equivalent to disambiguation if an UTP works as a dependent name.

What is not implementable is usage of an UTP as something else than a value outside of template-argument, without regular disambiguation, (with the exception of "down with typename" sites where an UTP, like any dependent name, would be parsed as a type, as the grammar element being parsed is type-id).

The UTP works exactly as a dependent name, but we need to bend the rules for template-argument using the Shrödinger's cat principle: If it can parse as at least two of id-expression, type-id and constant-expression we let it through at parsing and check the actual use at instantiation.

BengtGustafsson commented 1 year ago

Note that I initially wrote/said that he implemented eager checking based on the assumption that the disambiguation would be performed as for dependent names. Now he has invented a new syntax "T.as_type" for this purpose which I didn't know when I tested it, so in fact it is late checking, allowing disambiguated use inside a if constexpr checking the kind.

He uses a T.as_non_type too so with the non-standard disambiguation syntax he creates a situation where an UTP always have to be disambiguated and value kind is not assumed. This is maybe nicer but presuming value still works and is consistent with dependent names, so why not stay with it.

camaclean commented 1 year ago

I agree that we should stick with

T // value
typename T // type
typename auto T // UTP

for disambiguation. It's consistent with other dependent names. What he wrote seems to be more of a proof of concept thing. T.as_type etc has way too much potential to clash with members. It was probably just easier to write for now than adding code to parse a new syntax for typename auto T.

Should we also be writing a companion paper for std::is_value_v<typename auto T> and other utilities?

atomgalaxy commented 1 year ago

Nah, I don't thing we need a companion paper for those. They are required for use.

std::is_value_v<template auto T> std::is_type_v<template auto T> std::is_type_template_v<template auto T> std::is_variable_template_v<template auto T> std::is_concept_v<template auto T>

I think we need to allow the disambiguators. The T.to_type should obviously be spelled typename T :).

I think we can get away from requiring disambiguators when passing them on. Bengt had a great email thread about just specifying we don't check std::vector<T::foo> - I'd like to see if we can actually implement that. If we can, we can standardize it.

camaclean commented 1 year ago

I think we can specify that dependent names passed as template arguments are passed as universal template parameters:

template<template auto T, typename U>
void foo()
{
  std::is_value_v<T>;
  std::is_value_v<U::name>;
  std::vector<U::type>; // Ok, since U::type is passed as a UTP and will bind to `std::vector` as a type and if `U::type` is a value for some reason there will be a missmatch error
  std::vector<U::type*> // Error: Contains dependent name but what's being passed isn't a dependent name itself
  blah<U::value*2> // Ok. The expression contains a dependent name but isn't itself a dependent name, so U::value is parsed as a value and this works 
}

This slightly extends Down with typename! and I'm pretty sure this promotion to UTP works. We can specify that static_cast<decltype(auto)>(T) forces interpretation as a value, but that should be very rare.

camaclean commented 1 year ago

Actually, I don't think the value forcing is necessary, since there's not any conversion going on, it's just that dependent types passed as parameters have the type/value decision deferred. The decision happens eventually when the dependent names are used in an expression or passed to a non-UTP template argument, it's just delayed. A dependent name as a template argument can just be treated as a UTP.