Skip to content

[Clang] Check constraints for an explicit instantiation of a member function #104438

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 3 commits into from
Aug 19, 2024
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
3 changes: 2 additions & 1 deletion clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ C++ Specific Potentially Breaking Changes
template <> // error: extraneous template head
template <typename T>
void f();

ABI Changes in This Version
---------------------------

Expand Down Expand Up @@ -273,6 +273,7 @@ Bug Fixes to C++ Support
- Properly reject defaulted copy/move assignment operators that have a non-reference explicit object parameter.
- Clang now properly handles the order of attributes in `extern` blocks. (#GH101990).
- Fixed an assertion failure by preventing null explicit object arguments from being deduced. (#GH102025).
- Correctly check constraints of explicit instantiations of member functions. (#GH46029)

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
2 changes: 2 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -5667,6 +5667,8 @@ def err_explicit_instantiation_internal_linkage : Error<
def err_explicit_instantiation_not_known : Error<
"explicit instantiation of %0 does not refer to a function template, "
"variable template, member function, member class, or static data member">;
def err_explicit_instantiation_no_candidate : Error<
"no viable candidate for explicit instantiation of %0">;
def note_explicit_instantiation_here : Note<
"explicit instantiation refers here">;
def err_explicit_instantiation_data_member_not_instantiated : Error<
Expand Down
64 changes: 51 additions & 13 deletions clang/lib/Sema/SemaTemplate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10124,8 +10124,9 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
// instantiated from the member definition associated with its class
// template.
UnresolvedSet<8> TemplateMatches;
FunctionDecl *NonTemplateMatch = nullptr;
TemplateSpecCandidateSet FailedCandidates(D.getIdentifierLoc());
OverloadCandidateSet NonTemplateMatches(D.getBeginLoc(),
OverloadCandidateSet::CSK_Normal);
TemplateSpecCandidateSet FailedTemplateCandidates(D.getIdentifierLoc());
for (LookupResult::iterator P = Previous.begin(), PEnd = Previous.end();
P != PEnd; ++P) {
NamedDecl *Prev = *P;
Expand All @@ -10137,9 +10138,18 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
if (Method->getPrimaryTemplate()) {
TemplateMatches.addDecl(Method, P.getAccess());
} else {
// FIXME: Can this assert ever happen? Needs a test.
assert(!NonTemplateMatch && "Multiple NonTemplateMatches");
NonTemplateMatch = Method;
OverloadCandidate &C = NonTemplateMatches.addCandidate();
C.FoundDecl = P.getPair();
C.Function = Method;
C.Viable = true;
ConstraintSatisfaction S;
if (Method->getTrailingRequiresClause() &&
(CheckFunctionConstraints(Method, S, D.getIdentifierLoc(),
/*ForOverloadResolution=*/true) ||
!S.IsSatisfied)) {
C.Viable = false;
C.FailureKind = ovl_fail_constraints_not_satisfied;
}
}
}
}
Expand All @@ -10149,16 +10159,16 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
if (!FunTmpl)
continue;

TemplateDeductionInfo Info(FailedCandidates.getLocation());
TemplateDeductionInfo Info(FailedTemplateCandidates.getLocation());
FunctionDecl *Specialization = nullptr;
if (TemplateDeductionResult TDK = DeduceTemplateArguments(
FunTmpl, (HasExplicitTemplateArgs ? &TemplateArgs : nullptr), R,
Specialization, Info);
TDK != TemplateDeductionResult::Success) {
// Keep track of almost-matches.
FailedCandidates.addCandidate()
.set(P.getPair(), FunTmpl->getTemplatedDecl(),
MakeDeductionFailureInfo(Context, TDK, Info));
FailedTemplateCandidates.addCandidate().set(
P.getPair(), FunTmpl->getTemplatedDecl(),
MakeDeductionFailureInfo(Context, TDK, Info));
(void)TDK;
continue;
}
Expand All @@ -10172,7 +10182,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
CUDA().IdentifyTarget(Specialization,
/* IgnoreImplicitHDAttr = */ true) !=
CUDA().IdentifyTarget(D.getDeclSpec().getAttributes())) {
FailedCandidates.addCandidate().set(
FailedTemplateCandidates.addCandidate().set(
P.getPair(), FunTmpl->getTemplatedDecl(),
MakeDeductionFailureInfo(
Context, TemplateDeductionResult::CUDATargetMismatch, Info));
Expand All @@ -10182,12 +10192,40 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S,
TemplateMatches.addDecl(Specialization, P.getAccess());
}

FunctionDecl *Specialization = NonTemplateMatch;
FunctionDecl *Specialization = nullptr;
if (!NonTemplateMatches.empty()) {
unsigned Msg = 0;
OverloadCandidateDisplayKind DisplayKind;
OverloadCandidateSet::iterator Best;
switch (NonTemplateMatches.BestViableFunction(*this, D.getIdentifierLoc(),
Best)) {
case OR_Success:
case OR_Deleted:
Specialization = cast<FunctionDecl>(Best->Function);
break;
case OR_Ambiguous:
Msg = diag::err_explicit_instantiation_ambiguous;
DisplayKind = OCD_AmbiguousCandidates;
break;
case OR_No_Viable_Function:
Msg = diag::err_explicit_instantiation_no_candidate;
DisplayKind = OCD_AllCandidates;
break;
}
if (Msg) {
PartialDiagnostic Diag = PDiag(Msg) << Name;
NonTemplateMatches.NoteCandidates(
PartialDiagnosticAt(D.getIdentifierLoc(), Diag), *this, DisplayKind,
{});
return true;
}
}

if (!Specialization) {
// Find the most specialized function template specialization.
UnresolvedSetIterator Result = getMostSpecialized(
TemplateMatches.begin(), TemplateMatches.end(), FailedCandidates,
D.getIdentifierLoc(),
TemplateMatches.begin(), TemplateMatches.end(),
FailedTemplateCandidates, D.getIdentifierLoc(),
PDiag(diag::err_explicit_instantiation_not_known) << Name,
PDiag(diag::err_explicit_instantiation_ambiguous) << Name,
PDiag(diag::note_explicit_instantiation_candidate));
Expand Down
40 changes: 40 additions & 0 deletions clang/test/SemaTemplate/explicit-instantiation-cxx20.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s

template<class T>
concept C = true;

template<class T>
concept D = C<T> && true;

template<typename T>
struct a {
void no_candidate() requires(false) {}
// expected-note@-1 {{candidate function not viable: constraints not satisfied}} \
// expected-note@-1 {{because 'false' evaluated to false}}
void no_candidate() requires(false && false) {}
// expected-note@-1 {{candidate function not viable: constraints not satisfied}} \
// expected-note@-1 {{because 'false' evaluated to false}}

void subsumes();
void subsumes() requires C<T>;
void subsumes() requires D<T> {};

void ok() requires false;
void ok() requires true {};

void ok2() requires false;
void ok2(){};

void ambiguous() requires true;
// expected-note@-1 {{candidate function}}
void ambiguous() requires C<T>;
// expected-note@-1 {{candidate function}}
};
template void a<int>::no_candidate();
// expected-error@-1 {{no viable candidate for explicit instantiation of 'no_candidate'}}

template void a<int>::ambiguous();
// expected-error@-1 {{partial ordering for explicit instantiation of 'ambiguous' is ambiguous}}

template void a<int>::ok();
template void a<int>::ok2();
Loading