[concepts] Implement dcl.decl.general p4: No constraints on non-template funcs
The standard says: The optional requires-clause ([temp.pre]) in an init-declarator or member-declarator shall be present only if the declarator declares a templated function ([dcl.fct]). This implements that limitation, and updates the tests to the best of my ability to capture the intent of the original checks. Differential Revision: https://reviews.llvm.org/D125711
This commit is contained in:
@@ -365,6 +365,8 @@ C++ Language Changes in Clang
|
||||
of clang; use the ``-fclang-abi-compat=14`` option to get the old mangling.
|
||||
- Preprocessor character literals with a ``u8`` prefix are now correctly treated as
|
||||
unsigned character literals. This fixes `Issue 54886 <https://github.com/llvm/llvm-project/issues/54886>`_.
|
||||
- Stopped allowing constriants on non-template functions to be compliant with
|
||||
dcl.decl.general p4.
|
||||
|
||||
C++20 Feature Support
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -2853,6 +2853,8 @@ def err_constrained_virtual_method : Error<
|
||||
"virtual function cannot have a requires clause">;
|
||||
def err_trailing_requires_clause_on_deduction_guide : Error<
|
||||
"deduction guide cannot have a requires clause">;
|
||||
def err_constrained_non_templated_function
|
||||
: Error<"non-templated function cannot have a requires clause">;
|
||||
def err_reference_to_function_with_unsatisfied_constraints : Error<
|
||||
"invalid reference to function %0: constraints not satisfied">;
|
||||
def err_requires_expr_local_parameter_default_argument : Error<
|
||||
|
||||
@@ -11393,6 +11393,15 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
|
||||
checkThisInStaticMemberFunctionType(Method);
|
||||
}
|
||||
|
||||
// C++20: dcl.decl.general p4:
|
||||
// The optional requires-clause ([temp.pre]) in an init-declarator or
|
||||
// member-declarator shall be present only if the declarator declares a
|
||||
// templated function ([dcl.fct]).
|
||||
if (Expr *TRC = NewFD->getTrailingRequiresClause()) {
|
||||
if (!NewFD->isTemplated() && !NewFD->isTemplateInstantiation())
|
||||
Diag(TRC->getBeginLoc(), diag::err_constrained_non_templated_function);
|
||||
}
|
||||
|
||||
if (CXXConversionDecl *Conversion = dyn_cast<CXXConversionDecl>(NewFD))
|
||||
ActOnConversionDeclarator(Conversion);
|
||||
|
||||
|
||||
25
clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
Normal file
25
clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s
|
||||
|
||||
// expected-error@+2 {{non-templated function cannot have a requires clause}}
|
||||
void f1(int a)
|
||||
requires true;
|
||||
template <typename T>
|
||||
auto f2(T a) -> bool
|
||||
requires true; // OK
|
||||
|
||||
// expected-error@+4 {{trailing return type must appear before trailing requires clause}}
|
||||
template <typename T>
|
||||
auto f3(T a)
|
||||
requires true
|
||||
-> bool;
|
||||
|
||||
// expected-error@+2{{trailing requires clause can only be used when declaring a function}}
|
||||
void (*pf)()
|
||||
requires true;
|
||||
|
||||
// expected-error@+1{{trailing requires clause can only be used when declaring a function}}
|
||||
void g(int (*)() requires true);
|
||||
|
||||
// expected-error@+1{{expected expression}}
|
||||
auto *p = new void(*)(char)
|
||||
requires true;
|
||||
@@ -6,13 +6,40 @@ constexpr bool is_same_v = false;
|
||||
template<typename T>
|
||||
constexpr bool is_same_v<T, T> = true;
|
||||
|
||||
void f1(int a) requires true; // OK
|
||||
auto f2(int a) -> bool requires true; // OK
|
||||
using T = void ();
|
||||
|
||||
void f1non_templ(int a) requires true; // expected-error{{non-templated function cannot have a requires clause}}
|
||||
auto f2non_templ(int a) -> bool requires true; // expected-error{{non-templated function cannot have a requires clause}}
|
||||
auto f3non_templ(int a) -> bool (*)(int b) requires true; // expected-error{{non-templated function cannot have a requires clause}}
|
||||
// expected-error@+1{{non-templated function cannot have a requires clause}}
|
||||
auto f4_non_templ(int a) requires true -> bool; // expected-error{{trailing return type must appear before trailing requires clause}}
|
||||
void (f7non_templ()) requires true; // expected-error{{non-templated function cannot have a requires clause}}
|
||||
// expected-error@+1{{non-templated function cannot have a requires clause}}
|
||||
void (f8non_templ() requires true); // expected-error{{trailing requires clause should be placed outside parentheses}}
|
||||
// expected-error@+1{{non-templated function cannot have a requires clause}}
|
||||
T xnon_templ requires true;
|
||||
// expected-error@+2 2{{non-templated function cannot have a requires clause}}
|
||||
struct Snon_templ {
|
||||
T m1 requires true, m2 requires true;
|
||||
};
|
||||
|
||||
template <typename>
|
||||
void f1(int a)
|
||||
requires true; // OK
|
||||
|
||||
template <typename>
|
||||
auto f2(int a) -> bool
|
||||
requires true; // OK
|
||||
|
||||
template <typename>
|
||||
auto f3(int a) -> bool (*)(int b) requires true; // OK
|
||||
template <typename>
|
||||
auto f4(int a) requires true -> bool; // expected-error{{trailing return type must appear before trailing requires clause}}
|
||||
int f5(int a) requires; // expected-error{{expected expression}}
|
||||
int f6(int a) requires {} // expected-error{{expected expression}}
|
||||
template<typename>
|
||||
void (f7()) requires true;
|
||||
template<typename>
|
||||
void (f8() requires true); // expected-error{{trailing requires clause should be placed outside parentheses}}
|
||||
void (*(f9 requires (true)))(); // expected-error{{trailing requires clause should be placed outside parentheses}}
|
||||
static_assert(is_same_v<decltype(f9), void (*)()>);
|
||||
@@ -20,8 +47,14 @@ void (*pf)() requires true; // expected-error{{trailing requires clause can only
|
||||
void g1(int (*dsdads)() requires false); // expected-error{{trailing requires clause can only be used when declaring a function}}
|
||||
void g2(int (*(*dsdads)())() requires true); // expected-error{{trailing requires clause can only be used when declaring a function}}
|
||||
void g3(int (*(*dsdads)(int) requires true)() ); // expected-error{{trailing requires clause should be placed outside parentheses}}
|
||||
using T = void ();
|
||||
|
||||
template<typename U>
|
||||
struct Foo{
|
||||
T x requires true;
|
||||
};
|
||||
Foo<int> f;
|
||||
|
||||
template<typename>
|
||||
struct S {
|
||||
T m1 requires true, m2 requires true;
|
||||
};
|
||||
|
||||
@@ -2,21 +2,23 @@
|
||||
|
||||
namespace functions
|
||||
{
|
||||
void foo(int) requires false {}
|
||||
template<typename T>
|
||||
struct S {
|
||||
static void foo(int) requires false {}
|
||||
// expected-note@-1 3{{because 'false' evaluated to false}}
|
||||
// expected-note@-2 {{candidate function not viable: constraints not satisfied}}
|
||||
void bar(int) requires true {}
|
||||
static void bar(int) requires true {}
|
||||
};
|
||||
|
||||
void a(int);
|
||||
void a(double);
|
||||
|
||||
void baz() {
|
||||
foo(1); // expected-error{{no matching function for call to 'foo'}}
|
||||
bar(1);
|
||||
void (*p1)(int) = foo; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
|
||||
void (*p3)(int) = bar;
|
||||
decltype(foo)* a1 = nullptr; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
|
||||
decltype(bar)* a2 = nullptr;
|
||||
S<int>::foo(1); // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
|
||||
S<int>::bar(1);
|
||||
void (*p1)(int) = S<int>::foo; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
|
||||
void (*p3)(int) = S<int>::bar;
|
||||
decltype(S<int>::foo)* a1 = nullptr; // expected-error{{invalid reference to function 'foo': constraints not satisfied}}
|
||||
decltype(S<int>::bar)* a2 = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,51 +63,61 @@ namespace non_template
|
||||
template<typename T>
|
||||
concept AtMost8 = sizeof(T) <= 8;
|
||||
|
||||
template<typename T>
|
||||
int foo() requires AtLeast2<long> && AtMost8<long> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
double foo() requires AtLeast2<long> {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
double baz() requires AtLeast2<long> && AtMost8<long> { // expected-note {{candidate function}}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int baz() requires AtMost8<long> && AtLeast2<long> { // expected-note {{candidate function}}
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void bar() requires (sizeof(char[8]) >= 8) { }
|
||||
// expected-note@-1 {{candidate function}}
|
||||
// expected-note@-2 {{similar constraint expressions not considered equivalent}}
|
||||
|
||||
template<typename T>
|
||||
void bar() requires (sizeof(char[8]) >= 8 && sizeof(int) <= 30) { }
|
||||
// expected-note@-1 {{candidate function}}
|
||||
// expected-note@-2 {{similar constraint expression here}}
|
||||
|
||||
static_assert(is_same_v<decltype(foo()), int>);
|
||||
static_assert(is_same_v<decltype(baz()), int>); // expected-error {{call to 'baz' is ambiguous}}
|
||||
static_assert(is_same_v<decltype(bar()), void>); // expected-error {{call to 'bar' is ambiguous}}
|
||||
|
||||
static_assert(is_same_v<decltype(foo<int>()), int>);
|
||||
static_assert(is_same_v<decltype(baz<int>()), int>); // expected-error {{call to 'baz' is ambiguous}}
|
||||
static_assert(is_same_v<decltype(bar<int>()), void>); // expected-error {{call to 'bar' is ambiguous}}
|
||||
|
||||
template<typename T>
|
||||
constexpr int goo(int a) requires AtLeast2<int> && true {
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int goo(const int b) requires AtLeast2<int> {
|
||||
return 2;
|
||||
}
|
||||
|
||||
// Only trailing requires clauses of redeclarations are compared for overload resolution.
|
||||
template<typename T>
|
||||
constexpr int doo(int a, ...) requires AtLeast2<int> && true { // expected-note {{candidate function}}
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
constexpr int doo(int b) requires AtLeast2<int> { // expected-note {{candidate function}}
|
||||
return 2;
|
||||
}
|
||||
|
||||
static_assert(goo(1) == 1);
|
||||
static_assert(doo(2) == 1); // expected-error {{call to 'doo' is ambiguous}}
|
||||
static_assert(goo<int>(1) == 1);
|
||||
static_assert(doo<int>(2) == 1); // expected-error {{call to 'doo' is ambiguous}}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
// RUN: %clang_cc1 -std=c++2a -verify %s
|
||||
|
||||
struct S2 {};
|
||||
// expected-note@-1 {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'S1' to 'const S2' for 1st argument}}
|
||||
// expected-note@-2 {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'S1' to 'S2' for 1st argument}}
|
||||
// expected-note@-1 {{candidate constructor (the implicit copy constructor) not viable: no known conversion from 'S1<int>' to 'const S2' for 1st argument}}
|
||||
// expected-note@-2 {{candidate constructor (the implicit move constructor) not viable: no known conversion from 'S1<int>' to 'S2' for 1st argument}}
|
||||
// expected-note@-3 {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}}
|
||||
|
||||
template<typename T>
|
||||
struct S1 {
|
||||
void foo() const requires true {}
|
||||
void foo() const requires false {}
|
||||
@@ -18,12 +19,12 @@ struct S1 {
|
||||
};
|
||||
|
||||
void foo() {
|
||||
S1().foo();
|
||||
S1().bar();
|
||||
S1<int>().foo();
|
||||
S1<int>().bar();
|
||||
// expected-error@-1 {{invalid reference to function 'bar': constraints not satisfied}}
|
||||
(void) static_cast<bool>(S1());
|
||||
(void) static_cast<S2>(S1());
|
||||
// expected-error@-1 {{no matching conversion for static_cast from 'S1' to 'S2'}}
|
||||
(void) static_cast<bool>(S1<int>());
|
||||
(void) static_cast<S2>(S1<int>());
|
||||
// expected-error@-1 {{no matching conversion for static_cast from 'S1<int>' to 'S2'}}
|
||||
}
|
||||
|
||||
// Test that constraints are checked before implicit conversions are formed.
|
||||
@@ -35,9 +36,13 @@ struct A {
|
||||
operator T() {}
|
||||
};
|
||||
|
||||
void foo(int) requires false;
|
||||
void foo(A) requires true;
|
||||
template<typename>
|
||||
struct WrapsStatics {
|
||||
static void foo(int) requires false;
|
||||
static void foo(A) requires true;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct S {
|
||||
void foo(int) requires false;
|
||||
void foo(A) requires true;
|
||||
@@ -51,13 +56,13 @@ struct S {
|
||||
};
|
||||
|
||||
void bar() {
|
||||
foo(A{});
|
||||
S{1.}.foo(A{});
|
||||
WrapsStatics<int>::foo(A{});
|
||||
S<int>{1.}.foo(A{});
|
||||
// expected-error@-1{{invalid reference to function '~S': constraints not satisfied}}
|
||||
// Note - this behavior w.r.t. constrained dtors is a consequence of current
|
||||
// wording, which does not invoke overload resolution when a dtor is called.
|
||||
// P0848 is set to address this issue.
|
||||
S s = 1;
|
||||
S<int> s = 1;
|
||||
// expected-error@-1{{invalid reference to function '~S': constraints not satisfied}}
|
||||
int a = s;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,50 +12,48 @@ concept AtLeast2 = sizeof(T) >= 2;
|
||||
template<typename T>
|
||||
concept AtMost8 = sizeof(T) <= 8;
|
||||
|
||||
int foo() requires AtLeast2<long> && AtMost8<long> {
|
||||
template<typename T>
|
||||
struct S {
|
||||
static int foo() requires AtLeast2<long> && AtMost8<long> {
|
||||
return 0;
|
||||
}
|
||||
|
||||
double foo() requires AtLeast2<char> {
|
||||
static double foo() requires AtLeast2<char> {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
char bar() requires AtLeast2<char> { // expected-note {{possible target for call}}
|
||||
static char bar() requires AtLeast2<char> {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
short bar() requires AtLeast2<long> && AtMost8<long> {
|
||||
// expected-note@-1{{possible target for call}}
|
||||
// expected-note@-2{{candidate function}}
|
||||
static short bar() requires AtLeast2<long> && AtMost8<long> {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int bar() requires AtMost8<long> && AtLeast2<long> {
|
||||
// expected-note@-1{{possible target for call}}
|
||||
// expected-note@-2{{candidate function}}
|
||||
static int bar() requires AtMost8<long> && AtLeast2<long> {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
char baz() requires AtLeast2<char> {
|
||||
static char baz() requires AtLeast2<char> {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
short baz() requires AtLeast2<long> && AtMost8<long> {
|
||||
static short baz() requires AtLeast2<long> && AtMost8<long> {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
int baz() requires AtMost8<long> && AtLeast2<long> {
|
||||
static int baz() requires AtMost8<long> && AtLeast2<long> {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
long baz() requires AtMost8<long> && AtLeast2<long> && AtLeast2<short> {
|
||||
static long baz() requires AtMost8<long> && AtLeast2<long> && AtLeast2<short> {
|
||||
return 3.0;
|
||||
}
|
||||
};
|
||||
|
||||
void a() {
|
||||
static_assert(is_same_v<decltype(&foo), int(*)()>);
|
||||
static_assert(is_same_v<decltype(&bar), long(*)()>);
|
||||
// expected-error@-1{{reference to overloaded function could not be resolved; did you mean to call it with no arguments?}}
|
||||
// expected-error@-2{{call to 'bar' is ambiguous}}
|
||||
static_assert(is_same_v<decltype(&baz), long(*)()>);
|
||||
}
|
||||
static_assert(is_same_v<decltype(&S<int>::foo), int(*)()>);
|
||||
static_assert(is_same_v<decltype(&S<int>::bar), long(*)()>);
|
||||
// expected-error@-1{{reference to overloaded function could not be resolved; did you mean to call it?}}
|
||||
static_assert(is_same_v<decltype(&S<int>::baz), long(*)()>);
|
||||
}
|
||||
|
||||
@@ -139,8 +139,10 @@ template<typename T>
|
||||
void bar() requires (sizeof(T)) == 0;
|
||||
// expected-error@-1{{parentheses are required around this expression in a requires clause}}
|
||||
|
||||
template<typename T>
|
||||
void bar(int x, int y) requires (x, y, true);
|
||||
|
||||
template<typename T>
|
||||
struct B {
|
||||
int x;
|
||||
void foo(int y) requires (x, this, this->x, y, true);
|
||||
|
||||
@@ -130,6 +130,7 @@ bool r35 = requires { requires true };
|
||||
|
||||
bool r36 = requires (bool b) { requires sizeof(b) == 1; };
|
||||
|
||||
template<typename T>
|
||||
void r37(bool b) requires requires { 1 } {}
|
||||
// expected-error@-1 {{expected ';' at end of requirement}}
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
// RUN: %clang_cc1 -std=c++20 -verify %s
|
||||
|
||||
namespace P1972 {
|
||||
void f(int) requires false; // expected-note 4{{because 'false' evaluated to false}} \
|
||||
// expected-note{{constraints not satisfied}}
|
||||
template <typename T>
|
||||
struct S {
|
||||
static void f(int)
|
||||
requires false; // expected-note 4{{because 'false' evaluated to false}}
|
||||
};
|
||||
void g() {
|
||||
f(0); // expected-error{{no matching function for call to 'f'}}
|
||||
void (*p1)(int) = f; // expected-error{{invalid reference to function 'f': constraints not satisfied}}
|
||||
void (*p21)(int) = &f; // expected-error{{invalid reference to function 'f': constraints not satisfied}}
|
||||
decltype(f) *p2 = nullptr; // expected-error{{invalid reference to function 'f': constraints not satisfied}}
|
||||
S<int>::f(0); // expected-error{{invalid reference to function 'f': constraints not satisfied}}
|
||||
void (*p1)(int) = S<int>::f; // expected-error{{invalid reference to function 'f': constraints not satisfied}}
|
||||
void (*p21)(int) = &S<int>::f; // expected-error{{invalid reference to function 'f': constraints not satisfied}}
|
||||
decltype(S<int>::f) *p2 = nullptr; // expected-error{{invalid reference to function 'f': constraints not satisfied}}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user