Skip to content

[clang] Correctly(?) handle placeholder types in ExprRequirements. #4758

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions clang/lib/AST/ASTContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3378,8 +3378,9 @@ QualType ASTContext::getBlockPointerType(QualType T) const {
/// lvalue reference to the specified type.
QualType
ASTContext::getLValueReferenceType(QualType T, bool SpelledAsLValue) const {
assert(getCanonicalType(T) != OverloadTy &&
"Unresolved overloaded function type");
assert((!T->isPlaceholderType() ||
T->isSpecificPlaceholderType(BuiltinType::UnknownAny)) &&
"Unresolved placeholder type");

// Unique pointers, to guarantee there is only one pointer of a particular
// structure.
Expand Down Expand Up @@ -3417,6 +3418,10 @@ ASTContext::getLValueReferenceType(QualType T, bool SpelledAsLValue) const {
/// getRValueReferenceType - Return the uniqued reference to the type for an
/// rvalue reference to the specified type.
QualType ASTContext::getRValueReferenceType(QualType T) const {
assert((!T->isPlaceholderType() ||
T->isSpecificPlaceholderType(BuiltinType::UnknownAny)) &&
"Unresolved placeholder type");

// Unique pointers, to guarantee there is only one pointer of a particular
// structure.
llvm::FoldingSetNodeID ID;
Expand Down
3 changes: 3 additions & 0 deletions clang/lib/Sema/SemaTemplateInstantiate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1943,6 +1943,9 @@ TemplateInstantiator::TransformExprRequirement(concepts::ExprRequirement *Req) {
if (ExprInst.isInvalid())
return nullptr;
ExprResult TransExprRes = TransformExpr(E);
if (!TransExprRes.isInvalid() && !Trap.hasErrorOccurred() &&
TransExprRes.get()->hasPlaceholderType())
TransExprRes = SemaRef.CheckPlaceholderExpr(TransExprRes.get());
if (TransExprRes.isInvalid() || Trap.hasErrorOccurred())
TransExpr = createSubstDiag(SemaRef, Info, [&](llvm::raw_ostream &OS) {
E->printPretty(OS, nullptr, SemaRef.getPrintingPolicy());
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -12392,6 +12392,8 @@ TreeTransform<Derived>::TransformExprRequirement(concepts::ExprRequirement *Req)
TransExpr = Req->getExprSubstitutionDiagnostic();
else {
ExprResult TransExprRes = getDerived().TransformExpr(Req->getExpr());
if (TransExprRes.isUsable() && TransExprRes.get()->hasPlaceholderType())
TransExprRes = SemaRef.CheckPlaceholderExpr(TransExprRes.get());
if (TransExprRes.isInvalid())
return nullptr;
TransExpr = TransExprRes.get();
Expand Down
71 changes: 71 additions & 0 deletions clang/test/SemaTemplate/pr52909.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// RUN: %clang_cc1 -std=c++20 -verify %s
// RUN: %clang_cc1 -std=c++2b -verify %s

namespace PR52905 {
template <class> concept C = true;

struct A {
int begin();
int begin() const;
};

template <class T>
concept Beginable = requires (T t) {
{ t.begin } -> C;
// expected-note@-1 {{because 't.begin' would be invalid: reference to non-static member function must be called}}
};

static_assert(Beginable<A>); // expected-error {{static_assert failed}}
// expected-note@-1 {{does not satisfy 'Beginable'}}
} // namespace PR52905

namespace PR52909a {

template<class> constexpr bool B = true;
template<class T> concept True = B<T>;

template <class T>
int foo(T t) requires requires { // expected-note {{candidate template ignored: constraints not satisfied}}
{t.begin} -> True; // expected-note {{because 't.begin' would be invalid: reference to non-static member function must be called}}
}
{}

struct A { int begin(); };
auto x = foo(A()); // expected-error {{no matching function for call to 'foo'}}

} // namespace PR52909a

namespace PR52909b {

template<class> concept True = true;

template<class T> concept C = requires {
{ T::begin } -> True; // expected-note {{because 'T::begin' would be invalid: reference to overloaded function could not be resolved}}
};

struct A {
static void begin(int);
static void begin(double);
};

static_assert(C<A>); // expected-error {{static_assert failed}}
// expected-note@-1 {{because 'PR52909b::A' does not satisfy 'C'}}

} // namespace PR52909b

namespace PR53075 {
template<class> concept True = true;

template<class T> concept C = requires {
{ &T::f } -> True; // expected-note {{because '&T::f' would be invalid: reference to overloaded function could not be resolved}}
};

struct S {
int *f();
int *f() const;
};

static_assert(C<S>); // expected-error {{static_assert failed}}
// expected-note@-1 {{because 'PR53075::S' does not satisfy 'C'}}

} // namespace PR53075