Skip to content

Commit 06fe8f5

Browse files
committed
[Clang][Sema] Defer instantiation of exception specification until after partial ordering when determining primary template
1 parent a35599b commit 06fe8f5

File tree

5 files changed

+117
-21
lines changed

5 files changed

+117
-21
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,8 @@ Bug Fixes to C++ Support
281281
a requires-clause lie at the same depth as those of the surrounding lambda. This,
282282
in turn, results in the wrong template argument substitution during constraint checking.
283283
(`#78524 <https://github.com/llvm/llvm-project/issues/78524>`_)
284+
- Clang no longer instantiates the exception specification of discarded candidate function
285+
templates when determining the primary template of an explicit specialization.
284286

285287
Bug Fixes to AST Handling
286288
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9707,6 +9707,40 @@ bool Sema::CheckFunctionTemplateSpecialization(
97079707
// Ignore access information; it doesn't figure into redeclaration checking.
97089708
FunctionDecl *Specialization = cast<FunctionDecl>(*Result);
97099709

9710+
// C++23 [except.spec]p13:
9711+
// An exception specification is considered to be needed when:
9712+
// - [...]
9713+
// - the exception specification is compared to that of another declaration
9714+
// (e.g., an explicit specialization or an overriding virtual function);
9715+
// - [...]
9716+
//
9717+
// The exception specification of a defaulted function is evaluated as
9718+
// described above only when needed; similarly, the noexcept-specifier of a
9719+
// specialization of a function template or member function of a class
9720+
// template is instantiated only when needed.
9721+
//
9722+
// The standard doesn't specify what the "comparison with another declaration"
9723+
// entails, nor the exact circumstances in which it occurs. Moreover, it does
9724+
// not state which properties of an explicit specialization must match the
9725+
// primary template.
9726+
//
9727+
// We assume that an explicit specialization must correspond with (per
9728+
// [basic.scope.scope]p4) and declare the same entity as (per [basic.link]p8)
9729+
// the declaration produced by substitution into the function template.
9730+
//
9731+
// Since the determination whether two function declarations correspond does
9732+
// not consider exception specification, we only need to instantiate it once
9733+
// we determine the primary template when comparing types per
9734+
// [basic.link]p11.1.
9735+
auto *SpecializationFPT =
9736+
Specialization->getType()->castAs<FunctionProtoType>();
9737+
// If the function has a dependent exception specification, resolve it after
9738+
// we have selected the primary template so we can check whether it matches.
9739+
if (getLangOpts().CPlusPlus17 &&
9740+
isUnresolvedExceptionSpec(SpecializationFPT->getExceptionSpecType()) &&
9741+
!ResolveExceptionSpec(FD->getLocation(), SpecializationFPT))
9742+
return true;
9743+
97109744
FunctionTemplateSpecializationInfo *SpecInfo
97119745
= Specialization->getTemplateSpecializationInfo();
97129746
assert(SpecInfo && "Function template specialization info missing?");

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4632,11 +4632,9 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
46324632
Info.getLocation()))
46334633
return TemplateDeductionResult::MiscellaneousDeductionFailure;
46344634

4635-
// If the function has a dependent exception specification, resolve it now,
4636-
// so we can check that the exception specification matches.
46374635
auto *SpecializationFPT =
46384636
Specialization->getType()->castAs<FunctionProtoType>();
4639-
if (getLangOpts().CPlusPlus17 &&
4637+
if (IsAddressOfFunction && getLangOpts().CPlusPlus17 &&
46404638
isUnresolvedExceptionSpec(SpecializationFPT->getExceptionSpecType()) &&
46414639
!ResolveExceptionSpec(Info.getLocation(), SpecializationFPT))
46424640
return TemplateDeductionResult::MiscellaneousDeductionFailure;
@@ -4662,11 +4660,11 @@ TemplateDeductionResult Sema::DeduceTemplateArguments(
46624660
// specialization with respect to arguments of compatible pointer to function
46634661
// types, template argument deduction fails.
46644662
if (!ArgFunctionType.isNull()) {
4665-
if (IsAddressOfFunction
4666-
? !isSameOrCompatibleFunctionType(
4667-
Context.getCanonicalType(SpecializationType),
4668-
Context.getCanonicalType(ArgFunctionType))
4669-
: !Context.hasSameType(SpecializationType, ArgFunctionType)) {
4663+
if (IsAddressOfFunction ? !isSameOrCompatibleFunctionType(
4664+
Context.getCanonicalType(SpecializationType),
4665+
Context.getCanonicalType(ArgFunctionType))
4666+
: !Context.hasSameFunctionTypeIgnoringExceptionSpec(
4667+
SpecializationType, ArgFunctionType)) {
46704668
Info.FirstArg = TemplateArgument(SpecializationType);
46714669
Info.SecondArg = TemplateArgument(ArgFunctionType);
46724670
return TemplateDeductionResult::NonDeducedMismatch;
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// RUN: %clang_cc1 -fexceptions -fcxx-exceptions -fsyntax-only -verify %s
2+
3+
struct A {
4+
static constexpr bool x = true;
5+
};
6+
7+
namespace N0 {
8+
9+
template<typename T, typename U>
10+
void f(T, U) noexcept(T::y); // #1
11+
12+
template<typename T, typename U> // #2
13+
void f(T, U*) noexcept(T::x);
14+
15+
// Deduction should succeed for both candidates, and #2 should be selected as the primary template.
16+
// Only the exception specification of #2 should be instantiated.
17+
template<>
18+
void f(A, int*) noexcept;
19+
20+
}
21+
22+
namespace N1 {
23+
24+
template<typename T, typename U>
25+
void f(T, U) noexcept(T::x); // #1
26+
27+
template<typename T, typename U>
28+
void f(T, U*) noexcept(T::y); // #2
29+
// expected-error@-1 {{no member named 'y' in 'A'}}
30+
31+
// Deduction should succeed for both candidates, and #2 should be selected as the primary template.
32+
// Only the exception specification of #2 should be instantiated.
33+
template<>
34+
void f(A, int*) noexcept; // expected-error {{exception specification in declaration does not match previous declaration}}
35+
// expected-note@-1 {{in instantiation of exception specification for 'f<A, int>' requested here}}
36+
// expected-note@-2 {{previous declaration is here}}
37+
}
38+
39+
namespace N2 {
40+
41+
template<typename T, typename U>
42+
void f(T, U) noexcept(T::x);
43+
44+
template<typename T, typename U>
45+
void f(T, U*) noexcept(T::x);
46+
47+
template<typename T, typename U>
48+
void f(T, U**) noexcept(T::y); // expected-error {{no member named 'y' in 'A'}}
49+
50+
template<typename T, typename U>
51+
void f(T, U***) noexcept(T::x);
52+
53+
template<>
54+
void f(A, int*) noexcept; // expected-note {{previous declaration is here}}
55+
56+
template<>
57+
void f(A, int*); // expected-error {{'f<A, int>' is missing exception specification 'noexcept'}}
58+
59+
template<>
60+
void f(A, int**) noexcept; // expected-error {{exception specification in declaration does not match previous declaration}}
61+
// expected-note@-1 {{in instantiation of exception specification for 'f<A, int>' requested here}}
62+
// expected-note@-2 {{previous declaration is here}}
63+
64+
// FIXME: Exception specification is currently set to EST_None if instantiation fails.
65+
template<>
66+
void f(A, int**);
67+
68+
template<>
69+
void f(A, int***) noexcept; // expected-note {{previous declaration is here}}
70+
71+
template<>
72+
void f(A, int***); // expected-error {{'f<A, int>' is missing exception specification 'noexcept'}}
73+
74+
}

clang/test/SemaTemplate/class-template-noexcept.cpp

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22
// RUN: %clang_cc1 -std=c++11 -verify %s
33
// RUN: %clang_cc1 -std=c++17 -verify %s
44
// RUN: %clang_cc1 -std=c++1z -verify %s
5-
#if __cplusplus >= 201703
6-
// expected-no-diagnostics
7-
#endif
5+
86
class A {
97
public:
108
static const char X;
@@ -14,19 +12,9 @@ const char A::X = 0;
1412
template<typename U> void func() noexcept(U::X);
1513

1614
template<class... B, char x>
17-
#if __cplusplus >= 201703
18-
void foo(void(B...) noexcept(x)) {}
19-
#else
2015
void foo(void(B...) noexcept(x)) {} // expected-note{{candidate template ignored}}
21-
#endif
2216

2317
void bar()
2418
{
25-
#if __cplusplus >= 201703
26-
foo(func<A>);
27-
#else
2819
foo(func<A>); // expected-error{{no matching function for call}}
29-
#endif
3020
}
31-
32-

0 commit comments

Comments
 (0)