Skip to content

Commit bd2649a

Browse files
authored
[Clang] Convert ConstraintRefersToContainingTemplateChecker to using RAV (llvm#157062)
We previously employed a TreeTransform to perform a task that should have been achieved by RAV. The TreeTransform approach was a bit wasteful, as we discarded the transform result and incurred some incorrect semantic analysis. Fixes llvm#156225
1 parent 3386273 commit bd2649a

File tree

4 files changed

+74
-37
lines changed

4 files changed

+74
-37
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -342,6 +342,7 @@ Bug Fixes to C++ Support
342342
- Fix the parsing of variadic member functions when the ellipis immediately follows a default argument.(#GH153445)
343343
- Fixed a bug that caused ``this`` captured by value in a lambda with a dependent explicit object parameter to not be
344344
instantiated properly. (#GH154054)
345+
- Fixed a bug where our ``member-like constrained friend`` checking caused an incorrect analysis of lambda captures. (#GH156225)
345346
- Fixed a crash when implicit conversions from initialize list to arrays of
346347
unknown bound during constant evaluation. (#GH151716)
347348

clang/lib/AST/DynamicRecursiveASTVisitor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ using namespace clang;
8787
// ends up executing RAV's implementation because we used a qualified
8888
// function call.
8989
//
90-
// End result: RAV::TraverseCallExpr() is executed,
90+
// End result: RAV::TraverseCallExpr() is executed.
9191
namespace {
9292
template <bool Const> struct Impl : RecursiveASTVisitor<Impl<Const>> {
9393
DynamicRecursiveASTVisitorBase<Const> &Visitor;

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 54 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1716,79 +1716,98 @@ NamedDecl *Sema::ActOnTemplateTemplateParameter(
17161716

17171717
namespace {
17181718
class ConstraintRefersToContainingTemplateChecker
1719-
: public TreeTransform<ConstraintRefersToContainingTemplateChecker> {
1719+
: public ConstDynamicRecursiveASTVisitor {
1720+
using inherited = ConstDynamicRecursiveASTVisitor;
17201721
bool Result = false;
17211722
const FunctionDecl *Friend = nullptr;
17221723
unsigned TemplateDepth = 0;
17231724

17241725
// Check a record-decl that we've seen to see if it is a lexical parent of the
17251726
// Friend, likely because it was referred to without its template arguments.
1726-
void CheckIfContainingRecord(const CXXRecordDecl *CheckingRD) {
1727+
bool CheckIfContainingRecord(const CXXRecordDecl *CheckingRD) {
17271728
CheckingRD = CheckingRD->getMostRecentDecl();
17281729
if (!CheckingRD->isTemplated())
1729-
return;
1730+
return true;
17301731

17311732
for (const DeclContext *DC = Friend->getLexicalDeclContext();
17321733
DC && !DC->isFileContext(); DC = DC->getParent())
17331734
if (const auto *RD = dyn_cast<CXXRecordDecl>(DC))
1734-
if (CheckingRD == RD->getMostRecentDecl())
1735+
if (CheckingRD == RD->getMostRecentDecl()) {
17351736
Result = true;
1737+
return false;
1738+
}
1739+
1740+
return true;
17361741
}
17371742

1738-
void CheckNonTypeTemplateParmDecl(NonTypeTemplateParmDecl *D) {
1743+
bool CheckNonTypeTemplateParmDecl(const NonTypeTemplateParmDecl *D) {
17391744
if (D->getDepth() < TemplateDepth)
17401745
Result = true;
17411746

17421747
// Necessary because the type of the NTTP might be what refers to the parent
17431748
// constriant.
1744-
TransformType(D->getType());
1749+
return TraverseType(D->getType());
17451750
}
17461751

17471752
public:
1748-
using inherited = TreeTransform<ConstraintRefersToContainingTemplateChecker>;
1749-
1750-
ConstraintRefersToContainingTemplateChecker(Sema &SemaRef,
1751-
const FunctionDecl *Friend,
1753+
ConstraintRefersToContainingTemplateChecker(const FunctionDecl *Friend,
17521754
unsigned TemplateDepth)
1753-
: inherited(SemaRef), Friend(Friend), TemplateDepth(TemplateDepth) {}
1755+
: Friend(Friend), TemplateDepth(TemplateDepth) {}
1756+
17541757
bool getResult() const { return Result; }
17551758

17561759
// This should be the only template parm type that we have to deal with.
1757-
// SubstTempalteTypeParmPack, SubstNonTypeTemplateParmPack, and
1760+
// SubstTemplateTypeParmPack, SubstNonTypeTemplateParmPack, and
17581761
// FunctionParmPackExpr are all partially substituted, which cannot happen
17591762
// with concepts at this point in translation.
1760-
using inherited::TransformTemplateTypeParmType;
1761-
QualType TransformTemplateTypeParmType(TypeLocBuilder &TLB,
1762-
TemplateTypeParmTypeLoc TL, bool) {
1763-
if (TL.getDecl()->getDepth() < TemplateDepth)
1763+
bool VisitTemplateTypeParmType(const TemplateTypeParmType *Type) override {
1764+
if (Type->getDecl()->getDepth() < TemplateDepth) {
17641765
Result = true;
1765-
return inherited::TransformTemplateTypeParmType(
1766-
TLB, TL,
1767-
/*SuppressObjCLifetime=*/false);
1766+
return false;
1767+
}
1768+
return true;
17681769
}
17691770

1770-
Decl *TransformDecl(SourceLocation Loc, Decl *D) {
1771-
if (!D)
1772-
return D;
1771+
bool TraverseDeclRefExpr(const DeclRefExpr *E) override {
1772+
return TraverseDecl(E->getDecl());
1773+
}
1774+
1775+
bool TraverseTypedefType(const TypedefType *TT,
1776+
bool /*TraverseQualifier*/) override {
1777+
return TraverseType(TT->desugar());
1778+
}
1779+
1780+
bool TraverseTypeLoc(TypeLoc TL, bool TraverseQualifier) override {
1781+
// We don't care about TypeLocs. So traverse Types instead.
1782+
return TraverseType(TL.getType(), TraverseQualifier);
1783+
}
1784+
1785+
bool VisitTagType(const TagType *T) override {
1786+
return TraverseDecl(T->getOriginalDecl());
1787+
}
1788+
1789+
bool TraverseDecl(const Decl *D) override {
1790+
assert(D);
17731791
// FIXME : This is possibly an incomplete list, but it is unclear what other
17741792
// Decl kinds could be used to refer to the template parameters. This is a
17751793
// best guess so far based on examples currently available, but the
17761794
// unreachable should catch future instances/cases.
17771795
if (auto *TD = dyn_cast<TypedefNameDecl>(D))
1778-
TransformType(TD->getUnderlyingType());
1779-
else if (auto *NTTPD = dyn_cast<NonTypeTemplateParmDecl>(D))
1780-
CheckNonTypeTemplateParmDecl(NTTPD);
1781-
else if (auto *VD = dyn_cast<ValueDecl>(D))
1782-
TransformType(VD->getType());
1783-
else if (auto *TD = dyn_cast<TemplateDecl>(D))
1784-
TransformTemplateParameterList(TD->getTemplateParameters());
1785-
else if (auto *RD = dyn_cast<CXXRecordDecl>(D))
1786-
CheckIfContainingRecord(RD);
1787-
else if (isa<NamedDecl>(D)) {
1796+
return TraverseType(TD->getUnderlyingType());
1797+
if (auto *NTTPD = dyn_cast<NonTypeTemplateParmDecl>(D))
1798+
return CheckNonTypeTemplateParmDecl(NTTPD);
1799+
if (auto *VD = dyn_cast<ValueDecl>(D))
1800+
return TraverseType(VD->getType());
1801+
if (isa<TemplateDecl>(D))
1802+
return true;
1803+
if (auto *RD = dyn_cast<CXXRecordDecl>(D))
1804+
return CheckIfContainingRecord(RD);
1805+
1806+
if (isa<NamedDecl, RequiresExprBodyDecl>(D)) {
17881807
// No direct types to visit here I believe.
17891808
} else
17901809
llvm_unreachable("Don't know how to handle this declaration type yet");
1791-
return D;
1810+
return true;
17921811
}
17931812
};
17941813
} // namespace
@@ -1797,9 +1816,8 @@ bool Sema::ConstraintExpressionDependsOnEnclosingTemplate(
17971816
const FunctionDecl *Friend, unsigned TemplateDepth,
17981817
const Expr *Constraint) {
17991818
assert(Friend->getFriendObjectKind() && "Only works on a friend");
1800-
ConstraintRefersToContainingTemplateChecker Checker(*this, Friend,
1801-
TemplateDepth);
1802-
Checker.TransformExpr(const_cast<Expr *>(Constraint));
1819+
ConstraintRefersToContainingTemplateChecker Checker(Friend, TemplateDepth);
1820+
Checker.TraverseStmt(Constraint);
18031821
return Checker.getResult();
18041822
}
18051823

clang/test/SemaTemplate/concepts-friends.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -548,3 +548,21 @@ Template<void, 4> f{};
548548
static_assert(+Template<float, 5>{} == 5);
549549

550550
} // namespace GH78101
551+
552+
namespace GH156225 {
553+
554+
struct Test {
555+
template <class T>
556+
friend constexpr bool foo()
557+
requires([] {
558+
bool flags[1];
559+
for (bool x : flags)
560+
return false;
561+
return true;
562+
}())
563+
{
564+
return {};
565+
}
566+
};
567+
568+
}

0 commit comments

Comments
 (0)