Skip to content

Commit

Permalink
[Clang][Sema] Do not mark template parameters in the exception specif…
Browse files Browse the repository at this point in the history
…ication as used during partial ordering (#91534)

We do not deduce template arguments from the exception specification
when determining the primary template of a function template
specialization or when taking the address of a function template.
Therefore, this patch changes `isAtLeastAsSpecializedAs` such that we do
not mark template parameters in the exception specification as 'used'
during partial ordering (per [temp.deduct.partial]
p12) to prevent the following from being ambiguous:

```
template<typename T, typename U>
void f(U) noexcept(noexcept(T())); // #1

template<typename T>
void f(T*) noexcept; // #2

template<>
void f<int>(int*) noexcept; // currently ambiguous, selects #2 with this patch applied 
```

Although there is no corresponding wording in the standard (see core issue filed here
cplusplus/CWG#537), this seems
to be the intended behavior given the definition of _deduction
substitution loci_ in [temp.deduct.general] p7 (and EDG does the same thing).
  • Loading branch information
sdkrystian authored May 15, 2024
1 parent e00a3cc commit 667d12f
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 8 deletions.
3 changes: 3 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -713,6 +713,9 @@ Bug Fixes to C++ Support
- Correctly treat the compound statement of an ``if consteval`` as an immediate context. Fixes (#GH91509).
- When partial ordering alias templates against template template parameters,
allow pack expansions when the alias has a fixed-size parameter list. Fixes (#GH62529).
- Clang now ignores template parameters only used within the exception specification of candidate function
templates during partial ordering when deducing template arguments from a function declaration or when
taking the address of a function template.

Bug Fixes to AST Handling
^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
36 changes: 28 additions & 8 deletions clang/lib/Sema/SemaTemplateDeduction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5453,7 +5453,7 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
// is used.
if (DeduceTemplateArgumentsByTypeMatch(
S, TemplateParams, FD2->getType(), FD1->getType(), Info, Deduced,
TDF_None,
TDF_AllowCompatibleFunctionType,
/*PartialOrdering=*/true) != TemplateDeductionResult::Success)
return false;
break;
Expand Down Expand Up @@ -5485,20 +5485,40 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
switch (TPOC) {
case TPOC_Call:
for (unsigned I = 0, N = Args2.size(); I != N; ++I)
::MarkUsedTemplateParameters(S.Context, Args2[I], false,
TemplateParams->getDepth(),
UsedParameters);
::MarkUsedTemplateParameters(S.Context, Args2[I], /*OnlyDeduced=*/false,
TemplateParams->getDepth(), UsedParameters);
break;

case TPOC_Conversion:
::MarkUsedTemplateParameters(S.Context, Proto2->getReturnType(), false,
::MarkUsedTemplateParameters(S.Context, Proto2->getReturnType(),
/*OnlyDeduced=*/false,
TemplateParams->getDepth(), UsedParameters);
break;

case TPOC_Other:
::MarkUsedTemplateParameters(S.Context, FD2->getType(), false,
TemplateParams->getDepth(),
UsedParameters);
// We do not deduce template arguments from the exception specification
// when determining the primary template of a function template
// specialization or when taking the address of a function template.
// Therefore, we do not mark template parameters in the exception
// specification as used during partial ordering to prevent the following
// from being ambiguous:
//
// template<typename T, typename U>
// void f(U) noexcept(noexcept(T())); // #1
//
// template<typename T>
// void f(T*) noexcept; // #2
//
// template<>
// void f<int>(int*) noexcept; // explicit specialization of #2
//
// Although there is no corresponding wording in the standard, this seems
// to be the intended behavior given the definition of
// 'deduction substitution loci' in [temp.deduct].
::MarkUsedTemplateParameters(
S.Context,
S.Context.getFunctionTypeWithExceptionSpec(FD2->getType(), EST_None),
/*OnlyDeduced=*/false, TemplateParams->getDepth(), UsedParameters);
break;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// RUN: %clang_cc1 -fsyntax-only -verify %s
// expected-no-diagnostics

template<bool B>
struct A { };

constexpr A<false> a;
constexpr A<false> b;

constexpr int* x = nullptr;
constexpr short* y = nullptr;

namespace ExplicitArgs {
template<typename T, typename U>
constexpr int f(U) noexcept(noexcept(T())) {
return 0;
}

template<typename T>
constexpr int f(T*) noexcept {
return 1;
}

template<>
constexpr int f<int>(int*) noexcept {
return 2;
}

static_assert(f<int>(1) == 0);
static_assert(f<short>(y) == 1);
static_assert(f<int>(x) == 2);

template<typename T, typename U>
constexpr int g(U*) noexcept(noexcept(T())) {
return 3;
}

template<typename T>
constexpr int g(T) noexcept {
return 4;
}

template<>
constexpr int g<int>(int*) noexcept {
return 5;
}

static_assert(g<int>(y) == 3);
static_assert(g<short>(1) == 4);
static_assert(g<int>(x) == 5);
} // namespace ExplicitArgs

namespace DeducedArgs {
template<typename T, bool B>
constexpr int f(T, A<B>) noexcept(B) {
return 0;
}

template<typename T, bool B>
constexpr int f(T*, A<B>) noexcept(B && B) {
return 1;
}

template<>
constexpr int f(int*, A<false>) {
return 2;
}

static_assert(f<int*>(x, a) == 0);
static_assert(f<short>(y, a) == 1);
static_assert(f<int>(x, a) == 2);
} // namespace DeducedArgs

0 comments on commit 667d12f

Please sign in to comment.