Skip to content

Commit

Permalink
Correct deferred concepts with NTTP placeholder constraints
Browse files Browse the repository at this point in the history
Seemingly we never tested this, but the constraint on a NTTP was being
swtiched to the 'instantiated' version, but constraints need to be
relative to the 'top level', so this was causing us to not be able to
check the constraint on final use.

This patch corrects the issue by making the constraint created with the
un-instantiated version in the case of dependent constraint attachment.

Fixes: #61777
  • Loading branch information
Erich Keane committed Mar 30, 2023
1 parent 1f04aeb commit 18fe663
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 11 deletions.
3 changes: 2 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -8123,7 +8123,8 @@ class Sema final {
SourceLocation EllipsisLoc);

bool AttachTypeConstraint(AutoTypeLoc TL,
NonTypeTemplateParmDecl *ConstrainedParameter,
NonTypeTemplateParmDecl *NewConstrainedParm,
NonTypeTemplateParmDecl *OrigConstrainedParm,
SourceLocation EllipsisLoc);

bool RequireStructuralType(QualType T, SourceLocation Loc);
Expand Down
24 changes: 15 additions & 9 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1251,35 +1251,41 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
return false;
}

bool Sema::AttachTypeConstraint(AutoTypeLoc TL, NonTypeTemplateParmDecl *NTTP,
bool Sema::AttachTypeConstraint(AutoTypeLoc TL,
NonTypeTemplateParmDecl *NewConstrainedParm,
NonTypeTemplateParmDecl *OrigConstrainedParm,
SourceLocation EllipsisLoc) {
if (NTTP->getType() != TL.getType() ||
if (NewConstrainedParm->getType() != TL.getType() ||
TL.getAutoKeyword() != AutoTypeKeyword::Auto) {
Diag(NTTP->getTypeSourceInfo()->getTypeLoc().getBeginLoc(),
Diag(NewConstrainedParm->getTypeSourceInfo()->getTypeLoc().getBeginLoc(),
diag::err_unsupported_placeholder_constraint)
<< NTTP->getTypeSourceInfo()->getTypeLoc().getSourceRange();
<< NewConstrainedParm->getTypeSourceInfo()
->getTypeLoc()
.getSourceRange();
return true;
}
// FIXME: Concepts: This should be the type of the placeholder, but this is
// unclear in the wording right now.
DeclRefExpr *Ref =
BuildDeclRefExpr(NTTP, NTTP->getType(), VK_PRValue, NTTP->getLocation());
BuildDeclRefExpr(OrigConstrainedParm, OrigConstrainedParm->getType(),
VK_PRValue, OrigConstrainedParm->getLocation());
if (!Ref)
return true;
ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint(
*this, TL.getNestedNameSpecifierLoc(), TL.getConceptNameInfo(),
TL.getNamedConcept(), TL.getLAngleLoc(), TL.getRAngleLoc(),
BuildDecltypeType(Ref), NTTP->getLocation(),
BuildDecltypeType(Ref), OrigConstrainedParm->getLocation(),
[&](TemplateArgumentListInfo &ConstraintArgs) {
for (unsigned I = 0, C = TL.getNumArgs(); I != C; ++I)
ConstraintArgs.addArgument(TL.getArgLoc(I));
},
EllipsisLoc);
if (ImmediatelyDeclaredConstraint.isInvalid() ||
!ImmediatelyDeclaredConstraint.isUsable())
!ImmediatelyDeclaredConstraint.isUsable())
return true;

NTTP->setPlaceholderTypeConstraint(ImmediatelyDeclaredConstraint.get());
NewConstrainedParm->setPlaceholderTypeConstraint(
ImmediatelyDeclaredConstraint.get());
return false;
}

Expand Down Expand Up @@ -1559,7 +1565,7 @@ NamedDecl *Sema::ActOnNonTypeTemplateParameter(Scope *S, Declarator &D,

if (AutoTypeLoc TL = TInfo->getTypeLoc().getContainedAutoTypeLoc())
if (TL.isConstrained())
if (AttachTypeConstraint(TL, Param, D.getEllipsisLoc()))
if (AttachTypeConstraint(TL, Param, Param, D.getEllipsisLoc()))
Invalid = true;

if (Invalid)
Expand Down
4 changes: 3 additions & 1 deletion clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3008,8 +3008,10 @@ Decl *TemplateDeclInstantiator::VisitNonTypeTemplateParmDecl(

if (AutoTypeLoc AutoLoc = DI->getTypeLoc().getContainedAutoTypeLoc())
if (AutoLoc.isConstrained())
// Note: We attach the uninstantiated constriant here, so that it can be
// instantiated relative to the top level, like all our other constraints.
if (SemaRef.AttachTypeConstraint(
AutoLoc, Param,
AutoLoc, Param, D,
IsExpandedParameterPack
? DI->getTypeLoc().getAs<PackExpansionTypeLoc>()
.getEllipsisLoc()
Expand Down
24 changes: 24 additions & 0 deletions clang/test/SemaTemplate/concepts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -792,3 +792,27 @@ int foo() {
return b;
}
} // namespace GH48182

namespace GH61777 {
template<class T> concept C = sizeof(T) == 4; // #61777_C
template<class T, class U> concept C2 = sizeof(T) == sizeof(U); //#61777_C2

template<class T>
struct Parent {
template<class, C auto> struct TakesUnary { static const int i = 0 ; }; // #UNARY
template<class, C2<T> auto> struct TakesBinary { static const int i = 0 ; }; //#BINARY
};

static_assert(Parent<void>::TakesUnary<int, 0>::i == 0);
// expected-error@+3{{constraints not satisfied for class template 'TakesUnary'}}
// expected-note@#UNARY{{because 'decltype(0ULL)' (aka 'unsigned long long') does not satisfy 'C'}}
// expected-note@#61777_C{{because 'sizeof(unsigned long long) == 4' (8 == 4) evaluated to false}}
static_assert(Parent<void>::TakesUnary<int, 0uLL>::i == 0);

static_assert(Parent<int>::TakesBinary<int, 0>::i == 0);
// expected-error@+3{{constraints not satisfied for class template 'TakesBinary'}}
// expected-note@#BINARY{{because 'C2<decltype(0ULL), int>' evaluated to false}}
// expected-note@#61777_C2{{because 'sizeof(unsigned long long) == sizeof(int)' (8 == 4) evaluated to false}}
static_assert(Parent<int>::TakesBinary<int, 0ULL>::i == 0);
}

0 comments on commit 18fe663

Please sign in to comment.