Skip to content

Commit 3cb7b37

Browse files
[clang] Correctly(?) handle placeholder types in ExprRequirements. (#4758)
Bug llvm#52905 was originally papered over in a different way, but I believe this is the actually proper fix, or at least closer to it. We need to detect placeholder types as close to the front-end as possible, and cause them to fail constraints, rather than letting them persist into later stages. Fixes llvm#52905. Fixes llvm#52909. Fixes llvm#53075. Differential Revision: https://reviews.llvm.org/D118552 (cherry picked from commit f6ce456) Co-authored-by: Arthur O'Dwyer <[email protected]>
1 parent 2974a4e commit 3cb7b37

File tree

4 files changed

+83
-2
lines changed

4 files changed

+83
-2
lines changed

clang/lib/AST/ASTContext.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3378,8 +3378,9 @@ QualType ASTContext::getBlockPointerType(QualType T) const {
33783378
/// lvalue reference to the specified type.
33793379
QualType
33803380
ASTContext::getLValueReferenceType(QualType T, bool SpelledAsLValue) const {
3381-
assert(getCanonicalType(T) != OverloadTy &&
3382-
"Unresolved overloaded function type");
3381+
assert((!T->isPlaceholderType() ||
3382+
T->isSpecificPlaceholderType(BuiltinType::UnknownAny)) &&
3383+
"Unresolved placeholder type");
33833384

33843385
// Unique pointers, to guarantee there is only one pointer of a particular
33853386
// structure.
@@ -3417,6 +3418,10 @@ ASTContext::getLValueReferenceType(QualType T, bool SpelledAsLValue) const {
34173418
/// getRValueReferenceType - Return the uniqued reference to the type for an
34183419
/// rvalue reference to the specified type.
34193420
QualType ASTContext::getRValueReferenceType(QualType T) const {
3421+
assert((!T->isPlaceholderType() ||
3422+
T->isSpecificPlaceholderType(BuiltinType::UnknownAny)) &&
3423+
"Unresolved placeholder type");
3424+
34203425
// Unique pointers, to guarantee there is only one pointer of a particular
34213426
// structure.
34223427
llvm::FoldingSetNodeID ID;

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1943,6 +1943,9 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
19431943
if (ExprInst.isInvalid())
19441944
return nullptr;
19451945
ExprResult TransExprRes = TransformExpr(E);
1946+
if (!TransExprRes.isInvalid() && !Trap.hasErrorOccurred() &&
1947+
TransExprRes.get()->hasPlaceholderType())
1948+
TransExprRes = SemaRef.CheckPlaceholderExpr(TransExprRes.get());
19461949
if (TransExprRes.isInvalid() || Trap.hasErrorOccurred())
19471950
TransExpr = createSubstDiag(SemaRef, Info, [&](llvm::raw_ostream &OS) {
19481951
E->printPretty(OS, nullptr, SemaRef.getPrintingPolicy());

clang/lib/Sema/TreeTransform.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12392,6 +12392,8 @@ TreeTransform<Derived>::TransformExprRequirement(concepts::ExprRequirement *Req)
1239212392
TransExpr = Req->getExprSubstitutionDiagnostic();
1239312393
else {
1239412394
ExprResult TransExprRes = getDerived().TransformExpr(Req->getExpr());
12395+
if (TransExprRes.isUsable() && TransExprRes.get()->hasPlaceholderType())
12396+
TransExprRes = SemaRef.CheckPlaceholderExpr(TransExprRes.get());
1239512397
if (TransExprRes.isInvalid())
1239612398
return nullptr;
1239712399
TransExpr = TransExprRes.get();

clang/test/SemaTemplate/pr52909.cpp

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// RUN: %clang_cc1 -std=c++20 -verify %s
2+
// RUN: %clang_cc1 -std=c++2b -verify %s
3+
4+
namespace PR52905 {
5+
template <class> concept C = true;
6+
7+
struct A {
8+
int begin();
9+
int begin() const;
10+
};
11+
12+
template <class T>
13+
concept Beginable = requires (T t) {
14+
{ t.begin } -> C;
15+
// expected-note@-1 {{because 't.begin' would be invalid: reference to non-static member function must be called}}
16+
};
17+
18+
static_assert(Beginable<A>); // expected-error {{static_assert failed}}
19+
// expected-note@-1 {{does not satisfy 'Beginable'}}
20+
} // namespace PR52905
21+
22+
namespace PR52909a {
23+
24+
template<class> constexpr bool B = true;
25+
template<class T> concept True = B<T>;
26+
27+
template <class T>
28+
int foo(T t) requires requires { // expected-note {{candidate template ignored: constraints not satisfied}}
29+
{t.begin} -> True; // expected-note {{because 't.begin' would be invalid: reference to non-static member function must be called}}
30+
}
31+
{}
32+
33+
struct A { int begin(); };
34+
auto x = foo(A()); // expected-error {{no matching function for call to 'foo'}}
35+
36+
} // namespace PR52909a
37+
38+
namespace PR52909b {
39+
40+
template<class> concept True = true;
41+
42+
template<class T> concept C = requires {
43+
{ T::begin } -> True; // expected-note {{because 'T::begin' would be invalid: reference to overloaded function could not be resolved}}
44+
};
45+
46+
struct A {
47+
static void begin(int);
48+
static void begin(double);
49+
};
50+
51+
static_assert(C<A>); // expected-error {{static_assert failed}}
52+
// expected-note@-1 {{because 'PR52909b::A' does not satisfy 'C'}}
53+
54+
} // namespace PR52909b
55+
56+
namespace PR53075 {
57+
template<class> concept True = true;
58+
59+
template<class T> concept C = requires {
60+
{ &T::f } -> True; // expected-note {{because '&T::f' would be invalid: reference to overloaded function could not be resolved}}
61+
};
62+
63+
struct S {
64+
int *f();
65+
int *f() const;
66+
};
67+
68+
static_assert(C<S>); // expected-error {{static_assert failed}}
69+
// expected-note@-1 {{because 'PR53075::S' does not satisfy 'C'}}
70+
71+
} // namespace PR53075

0 commit comments

Comments
 (0)