Skip to content

Commit

Permalink
[P0857R0 Part-B] Allows `require' clauses appearing in
Browse files Browse the repository at this point in the history
template-template parameters. Although it effects whether a template can be
used as an argument for another template, the constraint seems not to
be checked, nor other major implementations (GCC, MSVC, et al.) check it.

Additionally, Part-A of the document seems to have been implemented.
So mark P0857R0 as completed.

Differential Revision: https://reviews.llvm.org/D134128
  • Loading branch information
limingliv authored and Erich Keane committed Oct 27, 2022
1 parent bba97c3 commit ae48d1c
Show file tree
Hide file tree
Showing 11 changed files with 124 additions and 62 deletions.
3 changes: 2 additions & 1 deletion clang/docs/ReleaseNotes.rst
Expand Up @@ -556,9 +556,10 @@ C++20 Feature Support
- Implemented `P0634r3 <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0634r3.html>`_,
which removes the requirement for the ``typename`` keyword in certain contexts.
- Implemented The Equality Operator You Are Looking For (`P2468 <http://wg21.link/p2468r2>`_).

- Implemented `P2113R0: Proposed resolution for 2019 comment CA 112 <https://wg21.link/P2113R0>`_
([temp.func.order]p6.2.1 is not implemented, matching GCC).
- Implemented `P0857R0 <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0857r0.html>`_,
which specifies constrained lambdas and constrained template *template-parameter*\s.

- Do not hide templated base members introduced via using-decl in derived class
(useful specially for constrained members). Fixes `GH50886 <https://github.com/llvm/llvm-project/issues/50886>`_.
Expand Down
10 changes: 6 additions & 4 deletions clang/include/clang/Sema/Sema.h
Expand Up @@ -7232,8 +7232,8 @@ class Sema final {
/// at least constrained than D2, and false otherwise.
///
/// \returns true if an error occurred, false otherwise.
bool IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef<const Expr *> AC1,
NamedDecl *D2, ArrayRef<const Expr *> AC2,
bool IsAtLeastAsConstrained(NamedDecl *D1, MutableArrayRef<const Expr *> AC1,
NamedDecl *D2, MutableArrayRef<const Expr *> AC2,
bool &Result);

/// If D1 was not at least as constrained as D2, but would've been if a pair
Expand Down Expand Up @@ -9076,7 +9076,8 @@ class Sema final {
const TemplateArgumentList *Innermost = nullptr,
bool RelativeToPrimary = false,
const FunctionDecl *Pattern = nullptr,
bool ForConstraintInstantiation = false);
bool ForConstraintInstantiation = false,
bool SkipForSpecialization = false);

/// A context in which code is being synthesized (where a source location
/// alone is not sufficient to identify the context). This covers template
Expand Down Expand Up @@ -9860,7 +9861,8 @@ class Sema final {

TemplateParameterList *
SubstTemplateParams(TemplateParameterList *Params, DeclContext *Owner,
const MultiLevelTemplateArgumentList &TemplateArgs);
const MultiLevelTemplateArgumentList &TemplateArgs,
bool EvaluateConstraints = true);

bool
SubstTemplateArguments(ArrayRef<TemplateArgumentLoc> Args,
Expand Down
32 changes: 21 additions & 11 deletions clang/lib/Parse/ParseTemplate.cpp
Expand Up @@ -874,27 +874,39 @@ NamedDecl *Parser::ParseTypeParameter(unsigned Depth, unsigned Position) {
/// template parameters.
///
/// type-parameter: [C++ temp.param]
/// 'template' '<' template-parameter-list '>' type-parameter-key
/// ...[opt] identifier[opt]
/// 'template' '<' template-parameter-list '>' type-parameter-key
/// identifier[opt] = id-expression
/// template-head type-parameter-key ...[opt] identifier[opt]
/// template-head type-parameter-key identifier[opt] = id-expression
/// type-parameter-key:
/// 'class'
/// 'typename' [C++1z]
NamedDecl *
Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) {
/// template-head: [C++2a]
/// 'template' '<' template-parameter-list '>'
/// requires-clause[opt]
NamedDecl *Parser::ParseTemplateTemplateParameter(unsigned Depth,
unsigned Position) {
assert(Tok.is(tok::kw_template) && "Expected 'template' keyword");

// Handle the template <...> part.
SourceLocation TemplateLoc = ConsumeToken();
SmallVector<NamedDecl*,8> TemplateParams;
SourceLocation LAngleLoc, RAngleLoc;
ExprResult OptionalRequiresClauseConstraintER;
{
MultiParseScope TemplateParmScope(*this);
if (ParseTemplateParameters(TemplateParmScope, Depth + 1, TemplateParams,
LAngleLoc, RAngleLoc)) {
return nullptr;
}
if (TryConsumeToken(tok::kw_requires)) {
OptionalRequiresClauseConstraintER =
Actions.ActOnRequiresClause(ParseConstraintLogicalOrExpression(
/*IsTrailingRequiresClause=*/false));
if (!OptionalRequiresClauseConstraintER.isUsable()) {
SkipUntil(tok::comma, tok::greater, tok::greatergreater,
StopAtSemi | StopBeforeMatch);
return nullptr;
}
}
}

// Provide an ExtWarn if the C++1z feature of using 'typename' here is used.
Expand Down Expand Up @@ -956,11 +968,9 @@ Parser::ParseTemplateTemplateParameter(unsigned Depth, unsigned Position) {
if (TryConsumeToken(tok::ellipsis, EllipsisLoc))
DiagnoseMisplacedEllipsis(EllipsisLoc, NameLoc, AlreadyHasEllipsis, true);

TemplateParameterList *ParamList =
Actions.ActOnTemplateParameterList(Depth, SourceLocation(),
TemplateLoc, LAngleLoc,
TemplateParams,
RAngleLoc, nullptr);
TemplateParameterList *ParamList = Actions.ActOnTemplateParameterList(
Depth, SourceLocation(), TemplateLoc, LAngleLoc, TemplateParams,
RAngleLoc, OptionalRequiresClauseConstraintER.get());

// Grab a default argument (if available).
// Per C++0x [basic.scope.pdecl]p9, we parse the default argument before
Expand Down
28 changes: 23 additions & 5 deletions clang/lib/Sema/SemaConcept.cpp
Expand Up @@ -586,12 +586,13 @@ bool Sema::CheckFunctionConstraints(const FunctionDecl *FD,
// Figure out the to-translation-unit depth for this function declaration for
// the purpose of seeing if they differ by constraints. This isn't the same as
// getTemplateDepth, because it includes already instantiated parents.
static unsigned CalculateTemplateDepthForConstraints(Sema &S,
const NamedDecl *ND) {
static unsigned
CalculateTemplateDepthForConstraints(Sema &S, const NamedDecl *ND,
bool SkipForSpecialization = false) {
MultiLevelTemplateArgumentList MLTAL = S.getTemplateInstantiationArgs(
ND, /*Final=*/false, /*Innermost=*/nullptr, /*RelativeToPrimary=*/true,
/*Pattern=*/nullptr,
/*ForConstraintInstantiation=*/true);
/*ForConstraintInstantiation=*/true, SkipForSpecialization);
return MLTAL.getNumSubstitutedLevels();
}

Expand Down Expand Up @@ -1278,8 +1279,10 @@ static bool subsumes(Sema &S, NamedDecl *DP, ArrayRef<const Expr *> P,
return false;
}

bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef<const Expr *> AC1,
NamedDecl *D2, ArrayRef<const Expr *> AC2,
bool Sema::IsAtLeastAsConstrained(NamedDecl *D1,
MutableArrayRef<const Expr *> AC1,
NamedDecl *D2,
MutableArrayRef<const Expr *> AC2,
bool &Result) {
if (AC1.empty()) {
Result = AC2.empty();
Expand All @@ -1298,6 +1301,21 @@ bool Sema::IsAtLeastAsConstrained(NamedDecl *D1, ArrayRef<const Expr *> AC1,
return false;
}

unsigned Depth1 = CalculateTemplateDepthForConstraints(*this, D1, true);
unsigned Depth2 = CalculateTemplateDepthForConstraints(*this, D2, true);

for (size_t I = 0; I != AC1.size() && I != AC2.size(); ++I) {
if (Depth2 > Depth1) {
AC1[I] = AdjustConstraintDepth(*this, Depth2 - Depth1)
.TransformExpr(const_cast<Expr *>(AC1[I]))
.get();
} else if (Depth1 > Depth2) {
AC2[I] = AdjustConstraintDepth(*this, Depth1 - Depth2)
.TransformExpr(const_cast<Expr *>(AC2[I]))
.get();
}
}

if (subsumes(*this, D1, AC1, D2, AC2, Result,
[this] (const AtomicConstraint &A, const AtomicConstraint &B) {
return A.subsumes(Context, B);
Expand Down
8 changes: 4 additions & 4 deletions clang/lib/Sema/SemaOverload.cpp
Expand Up @@ -10049,13 +10049,13 @@ bool clang::isBetterOverloadCandidate(
// parameter-type-lists, and F1 is more constrained than F2 [...],
if (!Cand1IsSpecialization && !Cand2IsSpecialization &&
sameFunctionParameterTypeLists(S, Cand1, Cand2)) {
Expr *RC1 = Cand1.Function->getTrailingRequiresClause();
Expr *RC2 = Cand2.Function->getTrailingRequiresClause();
const Expr *RC1 = Cand1.Function->getTrailingRequiresClause();
const Expr *RC2 = Cand2.Function->getTrailingRequiresClause();
if (RC1 && RC2) {
bool AtLeastAsConstrained1, AtLeastAsConstrained2;
if (S.IsAtLeastAsConstrained(Cand1.Function, {RC1}, Cand2.Function, {RC2},
if (S.IsAtLeastAsConstrained(Cand1.Function, RC1, Cand2.Function, RC2,
AtLeastAsConstrained1) ||
S.IsAtLeastAsConstrained(Cand2.Function, {RC2}, Cand1.Function, {RC1},
S.IsAtLeastAsConstrained(Cand2.Function, RC2, Cand1.Function, RC1,
AtLeastAsConstrained2))
return false;
if (AtLeastAsConstrained1 != AtLeastAsConstrained2)
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaTemplate.cpp
Expand Up @@ -5717,7 +5717,8 @@ bool Sema::CheckTemplateArgument(
Params =
SubstTemplateParams(Params, CurContext,
MultiLevelTemplateArgumentList(
Template, SugaredConverted, /*Final=*/true));
Template, SugaredConverted, /*Final=*/true),
/*EvaluateConstraints=*/false);
if (!Params)
return true;
}
Expand Down
36 changes: 21 additions & 15 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Expand Up @@ -75,7 +75,8 @@ struct Response {
// Add template arguments from a variable template instantiation.
Response
HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec,
MultiLevelTemplateArgumentList &Result) {
MultiLevelTemplateArgumentList &Result,
bool SkipForSpecialization) {
// For a class-scope explicit specialization, there are no template arguments
// at this level, but there may be enclosing template arguments.
if (VarTemplSpec->isClassScopeExplicitSpecialization())
Expand All @@ -93,16 +94,18 @@ HandleVarTemplateSpec(const VarTemplateSpecializationDecl *VarTemplSpec,
Specialized = VarTemplSpec->getSpecializedTemplateOrPartial();
if (VarTemplatePartialSpecializationDecl *Partial =
Specialized.dyn_cast<VarTemplatePartialSpecializationDecl *>()) {
Result.addOuterTemplateArguments(
Partial, VarTemplSpec->getTemplateInstantiationArgs().asArray(),
/*Final=*/false);
if (!SkipForSpecialization)
Result.addOuterTemplateArguments(
Partial, VarTemplSpec->getTemplateInstantiationArgs().asArray(),
/*Final=*/false);
if (Partial->isMemberSpecialization())
return Response::Done();
} else {
VarTemplateDecl *Tmpl = Specialized.get<VarTemplateDecl *>();
Result.addOuterTemplateArguments(
Tmpl, VarTemplSpec->getTemplateInstantiationArgs().asArray(),
/*Final=*/false);
if (!SkipForSpecialization)
Result.addOuterTemplateArguments(
Tmpl, VarTemplSpec->getTemplateInstantiationArgs().asArray(),
/*Final=*/false);
if (Tmpl->isMemberSpecialization())
return Response::Done();
}
Expand All @@ -126,17 +129,19 @@ HandleDefaultTempArgIntoTempTempParam(const TemplateTemplateParmDecl *TTP,
// Add template arguments from a class template instantiation.
Response
HandleClassTemplateSpec(const ClassTemplateSpecializationDecl *ClassTemplSpec,
MultiLevelTemplateArgumentList &Result) {
MultiLevelTemplateArgumentList &Result,
bool SkipForSpecialization) {
if (!ClassTemplSpec->isClassScopeExplicitSpecialization()) {
// We're done when we hit an explicit specialization.
if (ClassTemplSpec->getSpecializationKind() == TSK_ExplicitSpecialization &&
!isa<ClassTemplatePartialSpecializationDecl>(ClassTemplSpec))
return Response::Done();

Result.addOuterTemplateArguments(
const_cast<ClassTemplateSpecializationDecl *>(ClassTemplSpec),
ClassTemplSpec->getTemplateInstantiationArgs().asArray(),
/*Final=*/false);
if (!SkipForSpecialization)
Result.addOuterTemplateArguments(
const_cast<ClassTemplateSpecializationDecl *>(ClassTemplSpec),
ClassTemplSpec->getTemplateInstantiationArgs().asArray(),
/*Final=*/false);

// If this class template specialization was instantiated from a
// specialized member that is a class template, we're done.
Expand Down Expand Up @@ -279,7 +284,7 @@ Response HandleGenericDeclContext(const Decl *CurDecl) {
MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
const NamedDecl *ND, bool Final, const TemplateArgumentList *Innermost,
bool RelativeToPrimary, const FunctionDecl *Pattern,
bool ForConstraintInstantiation) {
bool ForConstraintInstantiation, bool SkipForSpecialization) {
assert(ND && "Can't find arguments for a decl if one isn't provided");
// Accumulate the set of template argument lists in this structure.
MultiLevelTemplateArgumentList Result;
Expand All @@ -295,10 +300,11 @@ MultiLevelTemplateArgumentList Sema::getTemplateInstantiationArgs(
Response R;
if (const auto *VarTemplSpec =
dyn_cast<VarTemplateSpecializationDecl>(CurDecl)) {
R = HandleVarTemplateSpec(VarTemplSpec, Result);
R = HandleVarTemplateSpec(VarTemplSpec, Result, SkipForSpecialization);
} else if (const auto *ClassTemplSpec =
dyn_cast<ClassTemplateSpecializationDecl>(CurDecl)) {
R = HandleClassTemplateSpec(ClassTemplSpec, Result);
R = HandleClassTemplateSpec(ClassTemplSpec, Result,
SkipForSpecialization);
} else if (const auto *Function = dyn_cast<FunctionDecl>(CurDecl)) {
R = HandleFunction(Function, Result, Pattern, RelativeToPrimary,
ForConstraintInstantiation);
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Expand Up @@ -4097,8 +4097,10 @@ TemplateDeclInstantiator::SubstTemplateParams(TemplateParameterList *L) {

TemplateParameterList *
Sema::SubstTemplateParams(TemplateParameterList *Params, DeclContext *Owner,
const MultiLevelTemplateArgumentList &TemplateArgs) {
const MultiLevelTemplateArgumentList &TemplateArgs,
bool EvaluateConstraints) {
TemplateDeclInstantiator Instantiator(*this, Owner, TemplateArgs);
Instantiator.setEvaluateConstraints(EvaluateConstraints);
return Instantiator.SubstTemplateParams(Params);
}

Expand Down
47 changes: 37 additions & 10 deletions clang/test/CXX/temp/temp.arg/temp.arg.template/p3-2a.cpp
@@ -1,22 +1,27 @@
// RUN: %clang_cc1 -std=c++2a -frelaxed-template-template-args -verify %s

template<typename T> concept C = T::f();
// expected-note@-1{{similar constraint}}
template<typename T> concept C = T::f(); // #C
template<typename T> concept D = C<T> && T::g();
template<typename T> concept F = T::f();
// expected-note@-1{{similar constraint expressions not considered equivalent}}
template<template<C> class P> struct S1 { }; // expected-note 2{{'P' declared here}}
template<typename T> concept F = T::f(); // #F
template<template<C> class P> struct S1 { }; // #S1

template<C> struct X { };

template<D> struct Y { }; // expected-note{{'Y' declared here}}
template<D> struct Y { }; // #Y
template<typename T> struct Z { };
template<F> struct W { }; // expected-note{{'W' declared here}}

template<F> struct W { }; // #W
S1<X> s11;
S1<Y> s12; // expected-error{{template template argument 'Y' is more constrained than template template parameter 'P'}}
S1<Y> s12;
// expected-error@-1 {{template template argument 'Y' is more constrained than template template parameter 'P'}}
// expected-note@#S1 {{'P' declared here}}
// expected-note@#Y {{'Y' declared here}}
S1<Z> s13;
S1<W> s14; // expected-error{{template template argument 'W' is more constrained than template template parameter 'P'}}
S1<W> s14;
// expected-error@-1 {{template template argument 'W' is more constrained than template template parameter 'P'}}
// expected-note@#S1 {{'P' declared here}}
// expected-note@#W {{'W' declared here}}
// expected-note@#F 1-2{{similar constraint expressions not considered equivalent}}
// expected-note@#C 1-2{{similar constraint}}

template<template<typename> class P> struct S2 { };

Expand All @@ -32,3 +37,25 @@ using N = typename T::type;

using s31 = S3<N>;
using s32 = S3<Z>;

template<template<typename T> requires C<T> class P> struct S4 { }; // #S4

S4<X> s41;
S4<Y> s42;
// expected-error@-1 {{template template argument 'Y' is more constrained than template template parameter 'P'}}
// expected-note@#S4 {{'P' declared here}}
// expected-note@#Y {{'Y' declared here}}
S4<Z> s43;
S4<W> s44;
// expected-error@-1 {{template template argument 'W' is more constrained than template template parameter 'P'}}
// expected-note@#S4 {{'P' declared here}}
// expected-note@#W {{'W' declared here}}

template<template<typename T> requires C<T> typename U> struct S5 {
template<typename T> static U<T> V;
};

struct Nothing {};

// FIXME: Wait the standard to clarify the intent.
template<> template<> Z<Nothing> S5<Z>::V<Nothing>;
9 changes: 4 additions & 5 deletions clang/test/SemaTemplate/concepts.cpp
Expand Up @@ -59,11 +59,10 @@ namespace P0857R0 {
x.operator()<false>(); // expected-error {{no matching member function}}
}

// FIXME: This is valid under P0857R0.
template<typename T> concept C = true;
template<template<typename T> requires C<T> typename U> struct X {}; // expected-error {{requires 'class'}} expected-error 0+{{}}
template<template<typename T> requires C<T> typename U> struct X {};
template<typename T> requires C<T> struct Y {};
X<Y> xy; // expected-error {{no template named 'X'}}
X<Y> xy;
}

namespace PR50306 {
Expand Down Expand Up @@ -706,7 +705,7 @@ Container<4>::var_templ<int> inst;
Container<5>::var_templ<int> inst_fail;
// expected-error@-1{{constraints not satisfied for alias template 'var_templ'}}
// expected-note@#CMVT_REQ{{because 'sizeof(int) == arity' (4 == 5) evaluated to false}}
} // namespace ConstrainedMemberVarTemplate
} // namespace ConstrainedMemberVarTemplate

// These should not diagnose, where we were unintentionally doing so before by
// checking trailing requires clause twice, yet not having the ability to the
Expand Down Expand Up @@ -764,4 +763,4 @@ struct __iterator_traits_member_pointer_or_arrow_or_void<_Ip> {
void use2() {
__iterator_traits_member_pointer_or_arrow_or_void<counted_iterator<int>> f;
}
}// namespace InheritedFromPartialSpec
}// namespace InheritedFromPartialSpec
6 changes: 1 addition & 5 deletions clang/www/cxx_status.html
Expand Up @@ -912,11 +912,7 @@ <h2 id="cxx20">C++20 implementation status</h2>
</tr>
<tr> <!-- from Albuquerque -->
<td><a href="https://wg21.link/p0857r0">P0857R0</a></td>
<td class="partial" align="center">
<details><summary>Partial</summary>
Constraining template template parameters is not yet supported.
</details>
</td>
<td rowspan="1" class="unreleased" align="center">Clang 16</td>
</tr>
<tr> <!-- from San Diego -->
<td><a href="https://wg21.link/p1084r2">P1084R2</a></td>
Expand Down

0 comments on commit ae48d1c

Please sign in to comment.