diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -365,6 +365,8 @@ 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 `_. +- Stopped allowing constriants on non-template functions to be compliant with + dcl.decl.general p4. C++20 Feature Support ^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2853,6 +2853,8 @@ "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< diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -11393,6 +11393,15 @@ 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(NewFD)) ActOnConversionDeclarator(Conversion); diff --git a/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp b/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/dcl.decl/dcl.decl.general/p4-20.cpp @@ -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 +auto f2(T a) -> bool + requires true; // OK + +// expected-error@+4 {{trailing return type must appear before trailing requires clause}} +template +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; diff --git a/clang/test/CXX/dcl/dcl.decl/p3.cpp b/clang/test/CXX/dcl/dcl.decl/p3.cpp --- a/clang/test/CXX/dcl/dcl.decl/p3.cpp +++ b/clang/test/CXX/dcl/dcl.decl/p3.cpp @@ -6,13 +6,40 @@ template constexpr bool is_same_v = 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 +void f1(int a) + requires true; // OK + +template +auto f2(int a) -> bool + requires true; // OK + +template auto f3(int a) -> bool (*)(int b) requires true; // OK +template 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 void (f7()) requires true; +template 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); @@ -20,8 +47,14 @@ 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 +struct Foo{ T x requires true; +}; +Foo f; + +template struct S { T m1 requires true, m2 requires true; }; diff --git a/clang/test/CXX/expr/expr.prim/expr.prim.id/p4.cpp b/clang/test/CXX/expr/expr.prim/expr.prim.id/p4.cpp --- a/clang/test/CXX/expr/expr.prim/expr.prim.id/p4.cpp +++ b/clang/test/CXX/expr/expr.prim/expr.prim.id/p4.cpp @@ -2,21 +2,23 @@ namespace functions { - void foo(int) requires false {} + template + 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::foo(1); // expected-error{{invalid reference to function 'foo': constraints not satisfied}} + S::bar(1); + void (*p1)(int) = S::foo; // expected-error{{invalid reference to function 'foo': constraints not satisfied}} + void (*p3)(int) = S::bar; + decltype(S::foo)* a1 = nullptr; // expected-error{{invalid reference to function 'foo': constraints not satisfied}} + decltype(S::bar)* a2 = nullptr; } } diff --git a/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp b/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp --- a/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp +++ b/clang/test/CXX/over/over.match/over.match.best/p1-2a.cpp @@ -63,51 +63,61 @@ template concept AtMost8 = sizeof(T) <= 8; + template int foo() requires AtLeast2 && AtMost8 { return 0; } + template double foo() requires AtLeast2 { return 0.0; } + template double baz() requires AtLeast2 && AtMost8 { // expected-note {{candidate function}} return 0.0; } + template int baz() requires AtMost8 && AtLeast2 { // expected-note {{candidate function}} return 0.0; } + template void bar() requires (sizeof(char[8]) >= 8) { } // expected-note@-1 {{candidate function}} // expected-note@-2 {{similar constraint expressions not considered equivalent}} + template 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); - static_assert(is_same_v); // expected-error {{call to 'baz' is ambiguous}} - static_assert(is_same_v); // expected-error {{call to 'bar' is ambiguous}} - + static_assert(is_same_v()), int>); + static_assert(is_same_v()), int>); // expected-error {{call to 'baz' is ambiguous}} + static_assert(is_same_v()), void>); // expected-error {{call to 'bar' is ambiguous}} + + template constexpr int goo(int a) requires AtLeast2 && true { return 1; } + template constexpr int goo(const int b) requires AtLeast2 { return 2; } // Only trailing requires clauses of redeclarations are compared for overload resolution. + template constexpr int doo(int a, ...) requires AtLeast2 && true { // expected-note {{candidate function}} return 1; } + template constexpr int doo(int b) requires AtLeast2 { // 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(1) == 1); + static_assert(doo(2) == 1); // expected-error {{call to 'doo' is ambiguous}} } diff --git a/clang/test/CXX/over/over.match/over.match.viable/p3.cpp b/clang/test/CXX/over/over.match/over.match.viable/p3.cpp --- a/clang/test/CXX/over/over.match/over.match.viable/p3.cpp +++ b/clang/test/CXX/over/over.match/over.match.viable/p3.cpp @@ -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' 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@-3 {{candidate constructor (the implicit default constructor) not viable: requires 0 arguments, but 1 was provided}} +template struct S1 { void foo() const requires true {} void foo() const requires false {} @@ -18,12 +19,12 @@ }; void foo() { - S1().foo(); - S1().bar(); + S1().foo(); + S1().bar(); // expected-error@-1 {{invalid reference to function 'bar': constraints not satisfied}} - (void) static_cast(S1()); - (void) static_cast(S1()); - // expected-error@-1 {{no matching conversion for static_cast from 'S1' to 'S2'}} + (void) static_cast(S1()); + (void) static_cast(S1()); + // expected-error@-1 {{no matching conversion for static_cast from 'S1' to 'S2'}} } // Test that constraints are checked before implicit conversions are formed. @@ -35,9 +36,13 @@ operator T() {} }; -void foo(int) requires false; -void foo(A) requires true; +template +struct WrapsStatics { + static void foo(int) requires false; + static void foo(A) requires true; +}; +template struct S { void foo(int) requires false; void foo(A) requires true; @@ -51,13 +56,13 @@ }; void bar() { - foo(A{}); - S{1.}.foo(A{}); + WrapsStatics::foo(A{}); + S{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 s = 1; // expected-error@-1{{invalid reference to function '~S': constraints not satisfied}} int a = s; -} \ No newline at end of file +} diff --git a/clang/test/CXX/over/over.over/p4-2a.cpp b/clang/test/CXX/over/over.over/p4-2a.cpp --- a/clang/test/CXX/over/over.over/p4-2a.cpp +++ b/clang/test/CXX/over/over.over/p4-2a.cpp @@ -12,50 +12,48 @@ template concept AtMost8 = sizeof(T) <= 8; -int foo() requires AtLeast2 && AtMost8 { +template +struct S { +static int foo() requires AtLeast2 && AtMost8 { return 0; } -double foo() requires AtLeast2 { +static double foo() requires AtLeast2 { return 0.0; } -char bar() requires AtLeast2 { // expected-note {{possible target for call}} +static char bar() requires AtLeast2 { return 1.0; } -short bar() requires AtLeast2 && AtMost8 { -// expected-note@-1{{possible target for call}} -// expected-note@-2{{candidate function}} +static short bar() requires AtLeast2 && AtMost8 { return 0.0; } -int bar() requires AtMost8 && AtLeast2 { -// expected-note@-1{{possible target for call}} -// expected-note@-2{{candidate function}} +static int bar() requires AtMost8 && AtLeast2 { return 0.0; } -char baz() requires AtLeast2 { +static char baz() requires AtLeast2 { return 1.0; } -short baz() requires AtLeast2 && AtMost8 { +static short baz() requires AtLeast2 && AtMost8 { return 0.0; } -int baz() requires AtMost8 && AtLeast2 { +static int baz() requires AtMost8 && AtLeast2 { return 0.0; } -long baz() requires AtMost8 && AtLeast2 && AtLeast2 { +static long baz() requires AtMost8 && AtLeast2 && AtLeast2 { return 3.0; } +}; void a() { - static_assert(is_same_v); - static_assert(is_same_v); - // 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); -} \ No newline at end of file + static_assert(is_same_v::foo), int(*)()>); + static_assert(is_same_v::bar), long(*)()>); + // expected-error@-1{{reference to overloaded function could not be resolved; did you mean to call it?}} + static_assert(is_same_v::baz), long(*)()>); +} diff --git a/clang/test/Parser/cxx-concepts-requires-clause.cpp b/clang/test/Parser/cxx-concepts-requires-clause.cpp --- a/clang/test/Parser/cxx-concepts-requires-clause.cpp +++ b/clang/test/Parser/cxx-concepts-requires-clause.cpp @@ -139,8 +139,10 @@ void bar() requires (sizeof(T)) == 0; // expected-error@-1{{parentheses are required around this expression in a requires clause}} +template void bar(int x, int y) requires (x, y, true); +template struct B { int x; void foo(int y) requires (x, this, this->x, y, true); diff --git a/clang/test/Parser/cxx2a-concepts-requires-expr.cpp b/clang/test/Parser/cxx2a-concepts-requires-expr.cpp --- a/clang/test/Parser/cxx2a-concepts-requires-expr.cpp +++ b/clang/test/Parser/cxx2a-concepts-requires-expr.cpp @@ -130,6 +130,7 @@ bool r36 = requires (bool b) { requires sizeof(b) == 1; }; +template void r37(bool b) requires requires { 1 } {} // expected-error@-1 {{expected ';' at end of requirement}} diff --git a/clang/test/SemaCXX/cxx20-check-fptr-constraints.cpp b/clang/test/SemaCXX/cxx20-check-fptr-constraints.cpp --- a/clang/test/SemaCXX/cxx20-check-fptr-constraints.cpp +++ b/clang/test/SemaCXX/cxx20-check-fptr-constraints.cpp @@ -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 +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::f(0); // expected-error{{invalid reference to function 'f': constraints not satisfied}} + void (*p1)(int) = S::f; // expected-error{{invalid reference to function 'f': constraints not satisfied}} + void (*p21)(int) = &S::f; // expected-error{{invalid reference to function 'f': constraints not satisfied}} + decltype(S::f) *p2 = nullptr; // expected-error{{invalid reference to function 'f': constraints not satisfied}} } }