Skip to content

Commit

Permalink
[concepts] Preserve the FoundDecl of ConceptReference properly (#85032)
Browse files Browse the repository at this point in the history
The `ConceptReference`'s `FoundDecl` claims it "can differ from
`NamedConcept` when, for example, the concept was found through a
`UsingShadowDecl`", but such the contract was not previously respected.

Fixes #82628
  • Loading branch information
zyn0217 committed Mar 14, 2024
1 parent c29b265 commit c1c8a0c
Show file tree
Hide file tree
Showing 9 changed files with 123 additions and 29 deletions.
28 changes: 28 additions & 0 deletions clang-tools-extra/clangd/unittests/SelectionTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "SourceCode.h"
#include "TestTU.h"
#include "support/TestTracer.h"
#include "clang/AST/ASTConcept.h"
#include "clang/AST/Decl.h"
#include "llvm/Support/Casting.h"
#include "gmock/gmock.h"
Expand Down Expand Up @@ -893,6 +894,33 @@ TEST(SelectionTest, DeclContextLambda) {
EXPECT_TRUE(ST.commonAncestor()->getDeclContext().isFunctionOrMethod());
}

TEST(SelectionTest, UsingConcepts) {
llvm::Annotations Test(R"cpp(
namespace ns {
template <typename T>
concept Foo = true;
}
using ns::Foo;
template <Fo^o... T, Fo^o auto U>
auto Func(Fo^o auto V) -> Fo^o decltype(auto) {
Fo^o auto W = V;
return W;
}
)cpp");
auto TU = TestTU::withCode(Test.code());
TU.ExtraArgs.emplace_back("-std=c++2c");
auto AST = TU.build();
for (auto Point : Test.points()) {
auto ST = SelectionTree::createRight(AST.getASTContext(), AST.getTokens(),
Point, Point);
auto *C = ST.commonAncestor()->ASTNode.get<ConceptReference>();
EXPECT_TRUE(C && C->getFoundDecl() &&
C->getFoundDecl()->getKind() == Decl::UsingShadow);
}
}

} // namespace
} // namespace clangd
} // namespace clang
4 changes: 1 addition & 3 deletions clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -548,9 +548,7 @@ TEST(WalkAST, Concepts) {
testWalk(Concept, "template<typename T> requires ^Foo<T> void func() {}");
testWalk(Concept, "template<typename T> void func() requires ^Foo<T> {}");
testWalk(Concept, "void func(^Foo auto x) {}");
// FIXME: Foo should be explicitly referenced.
testWalk("template<typename T> concept Foo = true;",
"void func() { ^Foo auto x = 1; }");
testWalk(Concept, "void func() { ^Foo auto x = 1; }");
}

TEST(WalkAST, FriendDecl) {
Expand Down
1 change: 1 addition & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ Bug Fixes to C++ Support

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
- Clang now properly preserves ``FoundDecls`` within a ``ConceptReference``. (#GH82628)

Miscellaneous Bug Fixes
^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -9215,7 +9215,7 @@ class Sema final {

bool AttachTypeConstraint(NestedNameSpecifierLoc NS,
DeclarationNameInfo NameInfo,
ConceptDecl *NamedConcept,
ConceptDecl *NamedConcept, NamedDecl *FoundDecl,
const TemplateArgumentListInfo *TemplateArgs,
TemplateTypeParmDecl *ConstrainedParameter,
SourceLocation EllipsisLoc);
Expand Down
6 changes: 5 additions & 1 deletion clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1192,9 +1192,13 @@ Sema::NameClassification Sema::ClassifyName(Scope *S, CXXScopeSpec &SS,
return ParsedType::make(T);
}

if (isa<ConceptDecl>(FirstDecl))
if (isa<ConceptDecl>(FirstDecl)) {
// We want to preserve the UsingShadowDecl for concepts.
if (auto *USD = dyn_cast<UsingShadowDecl>(Result.getRepresentativeDecl()))
return NameClassification::Concept(TemplateName(USD));
return NameClassification::Concept(
TemplateName(cast<TemplateDecl>(FirstDecl)));
}

if (auto *EmptyD = dyn_cast<UnresolvedUsingIfExistsDecl>(FirstDecl)) {
(void)DiagnoseUseOfDecl(EmptyD, NameLoc);
Expand Down
43 changes: 23 additions & 20 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,7 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,

TemplateName TN = TypeConstr->Template.get();
ConceptDecl *CD = cast<ConceptDecl>(TN.getAsTemplateDecl());
UsingShadowDecl *USD = TN.getAsUsingShadowDecl();

DeclarationNameInfo ConceptName(DeclarationName(TypeConstr->Name),
TypeConstr->TemplateNameLoc);
Expand All @@ -1174,15 +1175,15 @@ bool Sema::BuildTypeConstraint(const CXXScopeSpec &SS,
}
return AttachTypeConstraint(
SS.isSet() ? SS.getWithLocInContext(Context) : NestedNameSpecifierLoc(),
ConceptName, CD,
ConceptName, CD, /*FoundDecl=*/USD ? cast<NamedDecl>(USD) : CD,
TypeConstr->LAngleLoc.isValid() ? &TemplateArgs : nullptr,
ConstrainedParameter, EllipsisLoc);
}

template<typename ArgumentLocAppender>
template <typename ArgumentLocAppender>
static ExprResult formImmediatelyDeclaredConstraint(
Sema &S, NestedNameSpecifierLoc NS, DeclarationNameInfo NameInfo,
ConceptDecl *NamedConcept, SourceLocation LAngleLoc,
ConceptDecl *NamedConcept, NamedDecl *FoundDecl, SourceLocation LAngleLoc,
SourceLocation RAngleLoc, QualType ConstrainedType,
SourceLocation ParamNameLoc, ArgumentLocAppender Appender,
SourceLocation EllipsisLoc) {
Expand All @@ -1203,7 +1204,8 @@ static ExprResult formImmediatelyDeclaredConstraint(
SS.Adopt(NS);
ExprResult ImmediatelyDeclaredConstraint = S.CheckConceptTemplateId(
SS, /*TemplateKWLoc=*/SourceLocation(), NameInfo,
/*FoundDecl=*/NamedConcept, NamedConcept, &ConstraintArgs);
/*FoundDecl=*/FoundDecl ? FoundDecl : NamedConcept, NamedConcept,
&ConstraintArgs);
if (ImmediatelyDeclaredConstraint.isInvalid() || !EllipsisLoc.isValid())
return ImmediatelyDeclaredConstraint;

Expand Down Expand Up @@ -1233,7 +1235,7 @@ static ExprResult formImmediatelyDeclaredConstraint(
/// of arguments for the named concept).
bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,
DeclarationNameInfo NameInfo,
ConceptDecl *NamedConcept,
ConceptDecl *NamedConcept, NamedDecl *FoundDecl,
const TemplateArgumentListInfo *TemplateArgs,
TemplateTypeParmDecl *ConstrainedParameter,
SourceLocation EllipsisLoc) {
Expand All @@ -1246,24 +1248,24 @@ bool Sema::AttachTypeConstraint(NestedNameSpecifierLoc NS,

QualType ParamAsArgument(ConstrainedParameter->getTypeForDecl(), 0);

ExprResult ImmediatelyDeclaredConstraint =
formImmediatelyDeclaredConstraint(
*this, NS, NameInfo, NamedConcept,
TemplateArgs ? TemplateArgs->getLAngleLoc() : SourceLocation(),
TemplateArgs ? TemplateArgs->getRAngleLoc() : SourceLocation(),
ParamAsArgument, ConstrainedParameter->getLocation(),
[&] (TemplateArgumentListInfo &ConstraintArgs) {
if (TemplateArgs)
for (const auto &ArgLoc : TemplateArgs->arguments())
ConstraintArgs.addArgument(ArgLoc);
}, EllipsisLoc);
ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint(
*this, NS, NameInfo, NamedConcept, FoundDecl,
TemplateArgs ? TemplateArgs->getLAngleLoc() : SourceLocation(),
TemplateArgs ? TemplateArgs->getRAngleLoc() : SourceLocation(),
ParamAsArgument, ConstrainedParameter->getLocation(),
[&](TemplateArgumentListInfo &ConstraintArgs) {
if (TemplateArgs)
for (const auto &ArgLoc : TemplateArgs->arguments())
ConstraintArgs.addArgument(ArgLoc);
},
EllipsisLoc);
if (ImmediatelyDeclaredConstraint.isInvalid())
return true;

auto *CL = ConceptReference::Create(Context, /*NNS=*/NS,
/*TemplateKWLoc=*/SourceLocation{},
/*ConceptNameInfo=*/NameInfo,
/*FoundDecl=*/NamedConcept,
/*FoundDecl=*/FoundDecl,
/*NamedConcept=*/NamedConcept,
/*ArgsWritten=*/ArgsAsWritten);
ConstrainedParameter->setTypeConstraint(CL,
Expand Down Expand Up @@ -1293,8 +1295,9 @@ bool Sema::AttachTypeConstraint(AutoTypeLoc TL,
return true;
ExprResult ImmediatelyDeclaredConstraint = formImmediatelyDeclaredConstraint(
*this, TL.getNestedNameSpecifierLoc(), TL.getConceptNameInfo(),
TL.getNamedConcept(), TL.getLAngleLoc(), TL.getRAngleLoc(),
BuildDecltypeType(Ref), OrigConstrainedParm->getLocation(),
TL.getNamedConcept(), /*FoundDecl=*/TL.getFoundDecl(), TL.getLAngleLoc(),
TL.getRAngleLoc(), BuildDecltypeType(Ref),
OrigConstrainedParm->getLocation(),
[&](TemplateArgumentListInfo &ConstraintArgs) {
for (unsigned I = 0, C = TL.getNumArgs(); I != C; ++I)
ConstraintArgs.addArgument(TL.getArgLoc(I));
Expand Down Expand Up @@ -5415,7 +5418,7 @@ ExprResult Sema::BuildTemplateIdExpr(const CXXScopeSpec &SS,

if (R.getAsSingle<ConceptDecl>()) {
return CheckConceptTemplateId(SS, TemplateKWLoc, R.getLookupNameInfo(),
R.getFoundDecl(),
R.getRepresentativeDecl(),
R.getAsSingle<ConceptDecl>(), TemplateArgs);
}

Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2970,7 +2970,8 @@ bool Sema::SubstTypeConstraint(
}
return AttachTypeConstraint(
TC->getNestedNameSpecifierLoc(), TC->getConceptNameInfo(),
TC->getNamedConcept(), &InstArgs, Inst,
TC->getNamedConcept(),
/*FoundDecl=*/TC->getConceptReference()->getFoundDecl(), &InstArgs, Inst,
Inst->isParameterPack()
? cast<CXXFoldExpr>(TC->getImmediatelyDeclaredConstraint())
->getEllipsisLoc()
Expand Down
15 changes: 12 additions & 3 deletions clang/lib/Sema/SemaType.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3499,7 +3499,7 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
if (!Invalid) {
S.AttachTypeConstraint(
AutoLoc.getNestedNameSpecifierLoc(), AutoLoc.getConceptNameInfo(),
AutoLoc.getNamedConcept(),
AutoLoc.getNamedConcept(), /*FoundDecl=*/AutoLoc.getFoundDecl(),
AutoLoc.hasExplicitTemplateArgs() ? &TAL : nullptr,
InventedTemplateParam, D.getEllipsisLoc());
}
Expand All @@ -3525,11 +3525,17 @@ InventTemplateParameter(TypeProcessingState &state, QualType T,
}
}
if (!Invalid) {
UsingShadowDecl *USD =
TemplateId->Template.get().getAsUsingShadowDecl();
auto *CD =
cast<ConceptDecl>(TemplateId->Template.get().getAsTemplateDecl());
S.AttachTypeConstraint(
D.getDeclSpec().getTypeSpecScope().getWithLocInContext(S.Context),
DeclarationNameInfo(DeclarationName(TemplateId->Name),
TemplateId->TemplateNameLoc),
cast<ConceptDecl>(TemplateId->Template.get().getAsTemplateDecl()),
CD,
/*FoundDecl=*/
USD ? cast<NamedDecl>(USD) : CD,
TemplateId->LAngleLoc.isValid() ? &TemplateArgsInfo : nullptr,
InventedTemplateParam, D.getEllipsisLoc());
}
Expand Down Expand Up @@ -6423,9 +6429,12 @@ namespace {
DeclarationNameInfo DNI = DeclarationNameInfo(
TL.getTypePtr()->getTypeConstraintConcept()->getDeclName(),
TemplateId->TemplateNameLoc);
auto TN = TemplateId->Template.get();
auto *CR = ConceptReference::Create(
Context, NNS, TemplateId->TemplateKWLoc, DNI,
/*FoundDecl=*/nullptr,
/*FoundDecl=*/TN.getKind() == TemplateName::NameKind::UsingTemplate
? cast<NamedDecl>(TN.getAsUsingShadowDecl())
: cast_if_present<NamedDecl>(TN.getAsTemplateDecl()),
/*NamedDecl=*/TL.getTypePtr()->getTypeConstraintConcept(),
ASTTemplateArgumentListInfo::Create(Context, TemplateArgsInfo));
TL.setConceptReference(CR);
Expand Down
50 changes: 50 additions & 0 deletions clang/test/AST/ast-dump-concepts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,53 @@ struct Foo {
template <variadic_concept<int>... Ts>
Foo();
};

namespace GH82628 {
namespace ns {

template <typename T>
concept C = true;

} // namespace ns

using ns::C;

// CHECK: ConceptDecl {{.*}} Foo
// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} typename depth 0 index 0 T
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} UsingShadow {{.*}} 'C'
template <typename T>
concept Foo = C<T>;

// CHECK: TemplateTypeParmDecl {{.*}} Concept {{.*}} 'C' (UsingShadow {{.*}} 'C')
template <C T>
constexpr bool FooVar = false;

// CHECK: ConceptSpecializationExpr {{.*}} UsingShadow {{.*}} 'C'
template <typename T> requires C<T>
constexpr bool FooVar2 = true;

// CHECK: SimpleRequirement
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} UsingShadow {{.*}} 'C'
template <typename T> requires requires (T) { C<T>; }
constexpr bool FooVar3 = true;

// CHECK: NonTypeTemplateParmDecl
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} UsingShadow {{.*}} 'C'
template <C auto T>
constexpr bool FooVar4 = bool(T());

// CHECK: FunctionTemplateDecl
// CHECK-NEXT: |-TemplateTypeParmDecl {{.*}} Concept {{.*}} 'C' (UsingShadow {{.*}} 'C') depth 0 index 0 ... T
// CHECK: NonTypeTemplateParmDecl {{.*}} depth 0 index 1 U
// CHECK-NEXT: `-ConceptSpecializationExpr {{.*}} UsingShadow {{.*}} 'C'
// CHECK: |-TemplateTypeParmDecl {{.*}} Concept {{.*}} 'C' (UsingShadow {{.*}} 'C') depth 0 index 2 V:auto

template <C... T, C auto U>
auto FooFunc(C auto V) -> C decltype(auto) {
// FIXME: TypeLocs inside of the function body cannot be dumped via -ast-dump for now.
// See clang-tools-extra/clangd/unittests/SelectionTests.cpp:SelectionTest.UsingConcepts for their checkings.
C auto W = V;
return W;
}

}

0 comments on commit c1c8a0c

Please sign in to comment.