Issue |
122971
|
Summary |
wrong management of parameter substitution in a concept-id
|
Labels |
new issue
|
Assignees |
|
Reporter |
mrussoLuxoft
|
In the code reported below: (https://godbolt.org/z/TYKhzqhEf)
there are three couples of overloaded function templates, with different constraints.
Let's say that in the couples of func1 and func2, the first overload is designed to
work with std::set and the second with std::map.
However, func1<std::set<int>{}> leads to an error for Clang and gcc, because lambda expressions are not immediate context.
This is correctly related to the following standard text (current draft):
[expr.prim.req.general] - p5:
"... can result in the formation of invalid types or expressions in the immediate context of its requirements ... In such cases, the requires-_expression_ evaluates to false; it does not cause the program to be ill-formed."
[temp.constr.atomic] - p3:
"To determine if an atomic constraint is satisfied, the parameter mapping and template arguments are first substituted into its _expression_. If substitution results in an invalid type or _expression_ in the immediate context of the atomic constraint, the constraint is not satisfied. ... "
[temp.deduct.general] - p9:
"When substituting into a lambda-_expression_, substitution into its body is not in the immediate context. ..."
However, for func1b<std:::set<int>{}>, gcc and Clang behave differently. Indeed, Clang still considers the error in lambda _expression_ code, whereas gcc ignores it because this time the concept Cb does not use its parameters, relying on the following standard text:
[temp.constr.normal] - p(1.4):
"The normal form of a concept-id C<A1 , A2 , ..., An > is the normal form of the constraint-_expression_ of C, after substituting A1 , A2 , ..., An for C’s respective template parameters in the parameter mappings in each atomic constraint. ..."
which means that the substitution leads to no error if a parameter is not used.
I initially supposed this was an error for gcc, and posted a potential bug for gcc [here](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118398), but that discussion led to understand that's a problem of Clang.
gcc guys seemed to remember a known problem of Clang, but I tried to search with keywords 'concept', 'parameter', and others, and I could not find it. Hope I am not duplicating.
```
#include <iostream>
#include <map>
#include <set>
template<typename Container, typename KeyExtractor>
concept C = requires(Container c, KeyExtractor&& keyExtractor){
c.lower_bound(keyExtractor(c.begin()));
};
template<typename Container, typename KeyExtractor>
concept Cb = true;
template<typename Container>
requires C<Container, decltype([](Container::iterator it){return *it;})>
void func1([[maybe_unused]] const Container& c){
std::cout << "func1 first overload\n";
}
template<typename Container>
requires C<Container, decltype([](Container::iterator it){return it->first;})>
void func1([[maybe_unused]] const Container& c){
std::cout << "func1 second overload\n";
}
template<typename Container>
requires Cb<Container, decltype([](Container::iterator it){return *it;})>
void func1b([[maybe_unused]] const Container& c){
std::cout << "func1b first overload\n";
}
template<typename Container>
requires Cb<Container, decltype([](Container::iterator it){return it->first;})>
void func1b([[maybe_unused]] const Container& c){
std::cout << "func1b second overload\n";
}
template<typename Container>
requires requires(Container c){*c.begin();}
&& C<Container, decltype([](Container::iterator it){return *it;})>
void func2([[maybe_unused]] const Container& c)
{
std::cout << "func2 first overload\n";
}
template<typename Container>
requires requires(Container c){c.begin()->first;}
&& C<Container, decltype([](Container::iterator it){return it->first;})>
void func2([[maybe_unused]] const Container& c)
{
std::cout << "func2 second overload\n";
}
int main(){
func1(std::set<int>{}); // - gcc and clang manage lambda as non-immediate
// context, so getting a compilation error.
// - MVSC rejects second overload and selects the
// first one, that is, it considers failed
// constraints due to unfair "it->first" code
// in the lambda _expression_.
func1(std::map<int,int>{}); // all compilers correctly select second overload.
// This time, no reverse problem about "*it",
// and then concept C fails for set iterators.
func1b(std::set<int>{}); // - gcc considers both overloads as eligible,
// ignoring the part "it->first" for second overload,
// because it is not used in the concept definition.
// - clang consistently behaves instead as for func1.
// - MVSC rejects instead second overload, exactly as
// for func1.
//func1b(std::map<int,int>{}); // all compilers correctly consider ambiguous overloads.
func2(std::set<int>{}); // all compilers select first overload (i.e., gcc and clang
// behaves differently about lambda type resolution, because
// the result of the normal form for the constraints, is
// provided by a clause before the one containing the lambda)
func2(std::map<int,int>{}); // all compilers select second overload, as for func1.
}
```
_______________________________________________
llvm-bugs mailing list
llvm-bugs@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-bugs