llvm-project/clang/test/SemaCXX/cxx2c-template-template-param.cpp
Corentin Jabot 28ed57eda8
[Clang] Initial support for P2841 (Variable template and concept template parameters) (#150823)
This is a first pass at implementing
[P2841R7](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2841r7.pdf).

The implementation is far from complete; however, I'm aiming to do that
in chunks, to make our lives easier.

In particular, this does not implement
 - Subsumption
 - Mangling
- Satisfaction checking is minimal as we should focus on #141776 first
(note that I'm currently very stuck)

FTM, release notes, status page, etc, will be updated once the feature
is more mature. Given the state of the feature, it is not yet allowed in
older language modes.

Of note: 
- Mismatches between template template arguments and template template
parameters are a bit wonky. This is addressed by #130603
- We use `UnresolvedLookupExpr` to model template-id. While this is
pre-existing, I have been wondering if we want to introduce a different
OverloadExpr subclass for that. I did not make the change in this patch.
2025-08-04 08:51:22 +02:00

353 lines
13 KiB
C++

// RUN: %clang_cc1 -std=c++2c -verify %s
namespace Errors {
template <template<typename T> auto>
struct S1;
template <template<auto T> auto>
struct S2;
template <template<typename T> concept>
struct S3;
template <template<auto T> concept>
struct S4;
int a;
template <typename T>
concept C = true; // expected-note 2{{template argument refers to a concept 'C', here}}
template <typename T>
auto Var = 0; // expected-note 2{{template argument refers to a variable template 'Var', here}}
S1<1> t1; // expected-error {{template argument for template template parameter must be a variable template}}
S1<a> t2; // expected-error {{template argument for template template parameter must be a variable template}}
S1<int> t3; // expected-error {{template argument for template template parameter must be a variable template}}
S1<C> t4; // expected-error {{template argument does not refer to a variable template, or template template parameter}}
S2<1> t5; // expected-error {{template argument for template template parameter must be a variable template}}
S2<a> t6; // expected-error {{template argument for template template parameter must be a variable template}}
S2<int> t7; // expected-error {{template argument for template template parameter must be a variable template}}
S2<C> t8; // expected-error {{template argument does not refer to a variable template, or template template parameter}}
S3<1> t9; // expected-error {{template argument for template template parameter must be a concept}}
S3<a> t10; // expected-error {{template argument for template template parameter must be a concept}}
S3<int> t11; // expected-error {{template argument for template template parameter must be a concept}}
S3<Var> t12; // expected-error {{template argument does not refer to a concept, or template template parameter}}
S4<1> t13; // expected-error {{template argument for template template parameter must be a concept}}
S4<a> t14; // expected-error {{template argument for template template parameter must be a concept}}
S4<int> t15; // expected-error {{template argument for template template parameter must be a concept}}
S4<Var> t16; // expected-error {{template argument does not refer to a concept, or template template parameter}}
}
template <template<typename T> auto V> // expected-note {{previous template template parameter is here}} \
// expected-error{{template argument for non-type template parameter must be an expression}}
struct S1 {
static_assert(V<int> == 42);
static_assert(V<const int> == 84);
static_assert(V<double> == 0);
};
template <template<auto T> auto V> // expected-error {{template argument for template type parameter must be a type}} \
// expected-note {{previous template template parameter is here}}
struct S2 {
static_assert(V<0> == 1);
static_assert(V<1> == 0);
};
template <template<typename T> concept C > // expected-error {{template argument for non-type template parameter must be an expression}} \
// expected-note {{previous template template parameter is here}}
struct S3 {
static_assert(C<int>);
};
template <template<auto> concept C> // expected-error {{template argument for template type parameter must be a type}} \
// expected-note {{previous template template parameter is here}}
struct S4 {
static_assert(C<0>);
};
template <typename T> // expected-note {{template parameter is declared here}}
concept C = true;
template <auto I> // expected-note {{template parameter is declared here}}
concept CI = true;
template <typename T> // expected-note {{template parameter is declared here}}
constexpr auto Var = 42;
template <typename T>
constexpr auto Var<const T> = 84;
template <>
constexpr auto Var<double> = 0;
template <auto N> // expected-note {{template parameter is declared here}}
constexpr auto Var2 = 0;
template <auto N>
requires (N%2 == 0)
constexpr auto Var2<N> = 1;
void test () {
S1<Var2> sE; // expected-note {{template template argument has different template parameters than its corresponding template template parameter}}
S2<Var> sE; // expected-note {{template template argument has different template parameters than its corresponding template template parameter}}
S1<Var> s1;
S2<Var2> s2;
S3<C> s3;
S4<C> sE; // expected-note {{template template argument has different template parameters than its corresponding template template parameter}}
S4<CI> s4;
S3<CI> sE; // expected-note {{template template argument has different template parameters than its corresponding template template parameter}}
}
namespace template_type_constraints {
template <typename T>
concept Unary = true;
template <typename T, typename = int>
concept BinaryDefaulted = true;
template <typename T>
concept UnaryFalse = false; // expected-note 3{{because 'false' evaluated to false}}
template <typename T, typename = int>
concept BinaryDefaultedFalse = false;
template <template <typename...> concept C, typename T>
struct S {
template <C TT> // expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
void f(TT); // expected-note {{ignored}}
void g(C auto); // expected-note {{ignored}} \
// expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
auto h() -> C auto { // expected-error {{deduced type 'int' does not satisfy 'UnaryFalse'}}
return 0;
};
void test() {
C auto a = 0;
}
};
template <template <typename...> concept C, typename T>
struct SArg {
template <C<int> TT>
void f(TT);
void g(C<int> auto);
auto h() -> C<int> auto {
return 0;
};
void test() {
C<int> auto a = 0;
}
};
void test() {
S<Unary, int> s;
s.f(0);
s.g(0);
s.h();
S<BinaryDefaulted, int> s2;
s2.f(0);
s2.g(0);
s2.h();
SArg<BinaryDefaulted, int> s3;
s3.f(0);
s3.g(0);
s3.h();
}
void test_errors() {
S<UnaryFalse, int> s;
s.f(0); // expected-error {{no matching member function for call to 'f'}}
s.g(0); // expected-error {{no matching member function for call to 'g'}}
s.h(); // expected-note {{in instantiation of member function 'template_type_constraints::S<template_type_constraints::UnaryFalse, int>::h'}}
}
}
template <typename T>
concept Unary = true;
template <typename T, typename = int>
concept BinaryDefaulted = true;
template <typename T>
concept UnaryFalse = false; // expected-note 3{{because 'false' evaluated to false}}
template <typename T, typename = int>
concept BinaryDefaultedFalse = false;
template <template <typename...> concept C, typename T>
struct S {
template <C TT> // expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
void f(TT); // expected-note {{ignored}}
void g(C auto); // expected-note {{ignored}} \
// expected-note {{because 'int' does not satisfy 'UnaryFalse'}}
auto h() -> C auto { // expected-error {{deduced type 'int' does not satisfy 'UnaryFalse'}}
return 0;
};
void test() {
C auto a = 0;
}
};
template <template <typename...> concept C, typename T>
struct SArg {
template <C<int> TT>
void f(TT);
void g(C<int> auto);
auto h() -> C<int> auto {
return 0;
};
void test() {
C<int> auto a = 0;
}
};
void test_args() {
S<Unary, int> s;
s.f(0);
s.g(0);
s.h();
S<BinaryDefaulted, int> s2;
s2.f(0);
s2.g(0);
s2.h();
SArg<BinaryDefaulted, int> s3;
s3.f(0);
s3.g(0);
s3.h();
}
void test_errors() {
S<UnaryFalse, int> s;
s.f(0); // expected-error {{no matching member function for call to 'f'}}
s.g(0); // expected-error {{no matching member function for call to 'g'}}
s.h(); // expected-note {{in instantiation of member function 'S<UnaryFalse, int>::h'}}
}
namespace non_type {
template <auto>
concept Unary = true;
template <template <auto> concept C>
struct S {
template <C Foo> // expected-error {{concept named in type constraint is not a type concept}}
void f();
// FIXME, bad diagnostic
void g(C auto); // expected-error{{concept named in type constraint is not a type concept}}
auto h() -> C auto { // expected-error{{concept named in type constraint is not a type concept}}
}
void i() {
C auto a = 0; // expected-error{{concept named in type constraint is not a type concept}}
}
};
}
namespace default_args {
template <typename T>
concept Concept = false; // expected-note 2{{template argument refers to a concept 'Concept', here}} \
// expected-note 2{{because 'false' evaluated to false}}
template <typename T>
constexpr auto Var = false; // expected-note 2{{template argument refers to a variable template 'Var', here}}
template <typename T>
struct Type; // expected-note 2{{template argument refers to a class template 'Type', here}}
template <template <typename> auto = Concept> // expected-error {{template argument does not refer to a variable template, or template template parameter}}
struct E1;
template <template <typename> auto = Type> // expected-error {{template argument does not refer to a variable template, or template template parameter}}
struct E2;
template <template <typename> typename = Concept> // expected-error {{template argument does not refer to a class or alias template, or template template parameter}}
struct E3;
template <template <typename> typename = Var> // expected-error {{template argument does not refer to a class or alias template, or template template parameter}}
struct E4;
template <template <typename> concept = Var> // expected-error {{template argument does not refer to a concept, or template template parameter}}
struct E4;
template <template <typename> concept = Type> // expected-error {{template argument does not refer to a concept, or template template parameter}}
struct E4;
template <
template <typename> concept TConcept, // expected-note 2{{template argument refers to a concept 'TConcept', here}}
template <typename> auto TVar, // expected-note 2{{template argument refers to a variable template 'TVar', here}}
template <typename> typename TType // expected-note 2{{template argument refers to a class template 'TType', here}}
>
struct Nested {
template <template <typename> auto = TConcept> // expected-error {{template argument does not refer to a variable template, or template template parameter}}
struct E1;
template <template <typename> auto = TType> // expected-error {{template argument does not refer to a variable template, or template template parameter}}
struct E2;
template <template <typename> typename = TConcept> // expected-error {{template argument does not refer to a class or alias template, or template template parameter}}
struct E3;
template <template <typename> typename = TVar> // expected-error {{template argument does not refer to a class or alias template, or template template parameter}}
struct E4;
template <template <typename> concept = TVar> // expected-error {{template argument does not refer to a concept, or template template parameter}}
struct E4;
template <template <typename> concept = TType> // expected-error {{template argument does not refer to a concept, or template template parameter}}
struct E4;
};
template <template <typename> concept C = Concept>
struct TestDefaultConcept {
template <template <typename> concept CC = C>
void f() {
static_assert(C<int>); // expected-error {{static assertion failed}} \
// expected-note {{because 'int' does not satisfy 'Concept'}}
static_assert(CC<int>); // expected-error {{static assertion failed}} \
// expected-note {{because 'int' does not satisfy 'Concept'}}
}
};
void do_test_concept() {
TestDefaultConcept<>{}.f(); // expected-note {{in instantiation}}
}
template <template <typename> auto V = Var>
struct TestDefaultVar {
template <template <typename> auto VV = V>
void f() {
static_assert(V<int>); // expected-error {{static assertion failed}}
static_assert(VV<int>); // expected-error {{static assertion failed}}
}
};
void do_test_var() {
TestDefaultVar<>{}.f(); // expected-note {{in instantiation}}
}
}
namespace TTPDependence {
template <template <typename... > concept C>
concept A = C<>;
template <template <typename... > concept C>
concept B = C<int>;
template <template <typename... > auto Var>
concept C = Var<>;
template <template <typename... > auto Var>
concept D = Var<int>;
}
namespace InvalidName {
template <typename T, template <typename> concept C>
concept A = C<T>; // expected-note {{here}}
template <A<concept missing<int>> T> // expected-error {{expected expression}} \
// expected-error {{too few template arguments for concept 'A'}} \
// expected-error {{unknown type name 'T'}} \
// expected-error {{expected unqualified-id}}
auto f();
}