Skip to content

Commit aa7497a

Browse files
authored
[clang] check deduction consistency when partial ordering function templates (#100692)
This makes partial ordering of function templates consistent with other entities, by implementing [temp.deduct.type]p1 in that case. Fixes #18291
1 parent e61d606 commit aa7497a

12 files changed

+778
-319
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,8 @@ Bug Fixes to C++ Support
317317
- Clang now properly handles the order of attributes in `extern` blocks. (#GH101990).
318318
- Fixed an assertion failure by preventing null explicit object arguments from being deduced. (#GH102025).
319319
- Correctly check constraints of explicit instantiations of member functions. (#GH46029)
320+
- When performing partial ordering of function templates, clang now checks that
321+
the deduction was consistent. Fixes (#GH18291).
320322
- Fixed an assertion failure about a constraint of a friend function template references to a value with greater
321323
template depth than the friend function template. (#GH98258)
322324
- Clang now rebuilds the template parameters of out-of-line declarations and specializations in the context

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13283,6 +13283,10 @@ class Sema final : public SemaBase {
1328313283
/// \param AllowDeducedTST Whether a DeducedTemplateSpecializationType is
1328413284
/// acceptable as the top level type of the result.
1328513285
///
13286+
/// \param IsIncompleteSubstitution If provided, the pointee will be set
13287+
/// whenever substitution would perform a replacement with a null or
13288+
/// non-existent template argument.
13289+
///
1328613290
/// \returns If the instantiation succeeds, the instantiated
1328713291
/// type. Otherwise, produces diagnostics and returns a NULL type.
1328813292
TypeSourceInfo *SubstType(TypeSourceInfo *T,
@@ -13292,7 +13296,8 @@ class Sema final : public SemaBase {
1329213296

1329313297
QualType SubstType(QualType T,
1329413298
const MultiLevelTemplateArgumentList &TemplateArgs,
13295-
SourceLocation Loc, DeclarationName Entity);
13299+
SourceLocation Loc, DeclarationName Entity,
13300+
bool *IsIncompleteSubstitution = nullptr);
1329613301

1329713302
TypeSourceInfo *SubstType(TypeLoc TL,
1329813303
const MultiLevelTemplateArgumentList &TemplateArgs,

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 571 additions & 257 deletions
Large diffs are not rendered by default.

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 40 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1344,15 +1344,21 @@ namespace {
13441344
DeclarationName Entity;
13451345
// Whether to evaluate the C++20 constraints or simply substitute into them.
13461346
bool EvaluateConstraints = true;
1347+
// Whether Substitution was Incomplete, that is, we tried to substitute in
1348+
// any user provided template arguments which were null.
1349+
bool IsIncomplete = false;
1350+
// Whether an incomplete substituion should be treated as an error.
1351+
bool BailOutOnIncomplete;
13471352

13481353
public:
13491354
typedef TreeTransform<TemplateInstantiator> inherited;
13501355

13511356
TemplateInstantiator(Sema &SemaRef,
13521357
const MultiLevelTemplateArgumentList &TemplateArgs,
1353-
SourceLocation Loc, DeclarationName Entity)
1358+
SourceLocation Loc, DeclarationName Entity,
1359+
bool BailOutOnIncomplete = false)
13541360
: inherited(SemaRef), TemplateArgs(TemplateArgs), Loc(Loc),
1355-
Entity(Entity) {}
1361+
Entity(Entity), BailOutOnIncomplete(BailOutOnIncomplete) {}
13561362

13571363
void setEvaluateConstraints(bool B) {
13581364
EvaluateConstraints = B;
@@ -1374,6 +1380,9 @@ namespace {
13741380
/// Returns the name of the entity being instantiated, if any.
13751381
DeclarationName getBaseEntity() { return Entity; }
13761382

1383+
/// Returns whether any substitution so far was incomplete.
1384+
bool getIsIncomplete() const { return IsIncomplete; }
1385+
13771386
/// Sets the "base" location and entity when that
13781387
/// information is known based on another transformation.
13791388
void setBase(SourceLocation Loc, DeclarationName Entity) {
@@ -1420,6 +1429,10 @@ namespace {
14201429
if (TemplateArgs.hasTemplateArgument(Depth, Index)) {
14211430
Result = TemplateArgs(Depth, Index);
14221431
TemplateArgs.setArgument(Depth, Index, TemplateArgument());
1432+
} else {
1433+
IsIncomplete = true;
1434+
if (BailOutOnIncomplete)
1435+
return TemplateArgument();
14231436
}
14241437
}
14251438

@@ -1820,8 +1833,10 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
18201833
// template arguments in a function template, but there were some
18211834
// arguments left unspecified.
18221835
if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
1823-
TTP->getPosition()))
1824-
return D;
1836+
TTP->getPosition())) {
1837+
IsIncomplete = true;
1838+
return BailOutOnIncomplete ? nullptr : D;
1839+
}
18251840

18261841
TemplateArgument Arg = TemplateArgs(TTP->getDepth(), TTP->getPosition());
18271842

@@ -1967,8 +1982,10 @@ TemplateName TemplateInstantiator::TransformTemplateName(
19671982
// template arguments in a function template, but there were some
19681983
// arguments left unspecified.
19691984
if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
1970-
TTP->getPosition()))
1971-
return Name;
1985+
TTP->getPosition())) {
1986+
IsIncomplete = true;
1987+
return BailOutOnIncomplete ? TemplateName() : Name;
1988+
}
19721989

19731990
TemplateArgument Arg = TemplateArgs(TTP->getDepth(), TTP->getPosition());
19741991

@@ -2050,8 +2067,10 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
20502067
// template arguments in a function template, but there were some
20512068
// arguments left unspecified.
20522069
if (!TemplateArgs.hasTemplateArgument(NTTP->getDepth(),
2053-
NTTP->getPosition()))
2054-
return E;
2070+
NTTP->getPosition())) {
2071+
IsIncomplete = true;
2072+
return BailOutOnIncomplete ? ExprError() : E;
2073+
}
20552074

20562075
TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition());
20572076

@@ -2470,6 +2489,10 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
24702489
// template arguments in a function template class, but there were some
24712490
// arguments left unspecified.
24722491
if (!TemplateArgs.hasTemplateArgument(T->getDepth(), T->getIndex())) {
2492+
IsIncomplete = true;
2493+
if (BailOutOnIncomplete)
2494+
return QualType();
2495+
24732496
TemplateTypeParmTypeLoc NewTL
24742497
= TLB.push<TemplateTypeParmTypeLoc>(TL.getType());
24752498
NewTL.setNameLoc(TL.getNameLoc());
@@ -2841,7 +2864,8 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL,
28412864
/// Deprecated form of the above.
28422865
QualType Sema::SubstType(QualType T,
28432866
const MultiLevelTemplateArgumentList &TemplateArgs,
2844-
SourceLocation Loc, DeclarationName Entity) {
2867+
SourceLocation Loc, DeclarationName Entity,
2868+
bool *IsIncompleteSubstitution) {
28452869
assert(!CodeSynthesisContexts.empty() &&
28462870
"Cannot perform an instantiation without some context on the "
28472871
"instantiation stack");
@@ -2851,8 +2875,13 @@ QualType Sema::SubstType(QualType T,
28512875
if (!T->isInstantiationDependentType() && !T->isVariablyModifiedType())
28522876
return T;
28532877

2854-
TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, Entity);
2855-
return Instantiator.TransformType(T);
2878+
TemplateInstantiator Instantiator(
2879+
*this, TemplateArgs, Loc, Entity,
2880+
/*BailOutOnIncomplete=*/IsIncompleteSubstitution != nullptr);
2881+
QualType QT = Instantiator.TransformType(T);
2882+
if (IsIncompleteSubstitution && Instantiator.getIsIncomplete())
2883+
*IsIncompleteSubstitution = true;
2884+
return QT;
28562885
}
28572886

28582887
static bool NeedsInstantiationAsFunctionType(TypeSourceInfo *T) {

clang/test/CodeCompletion/variadic-template.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ void f() {
88
// The important thing is that we provide OVERLOAD signature in all those cases.
99
//
1010
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-5):7 %s -o - | FileCheck --check-prefix=CHECK-1 %s
11-
// CHECK-1: OVERLOAD: [#void#]fun(<#T x#>, Args args...)
11+
// CHECK-1: OVERLOAD: [#void#]fun(<#T x#>)
1212
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-7):10 %s -o - | FileCheck --check-prefix=CHECK-2 %s
1313
// CHECK-2: OVERLOAD: [#void#]fun(int x)
1414
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:%(line-9):13 %s -o - | FileCheck --check-prefix=CHECK-3 %s

clang/test/Index/complete-call.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -409,7 +409,8 @@ struct Bar2Template : public BarTemplates {
409409
// CHECK-CC22-NEXT: Objective-C interface
410410

411411
// RUN: c-index-test -code-completion-at=%s:64:10 %s | FileCheck -check-prefix=CHECK-CC23 %s
412-
// CHECK-CC23: OverloadCandidate:{ResultType void}{Text foo_12}{LeftParen (}{CurrentParameter int}{Comma , }{Placeholder int}{RightParen )} (1)
412+
413+
// CHECK-CC23: OverloadCandidate:{ResultType void}{Text foo_12}{LeftParen (}{CurrentParameter T}{Comma , }{Placeholder T}{RightParen )} (1)
413414
// CHECK-CC23: Completion contexts:
414415
// CHECK-CC23-NEXT: Any type
415416
// CHECK-CC23-NEXT: Any value
@@ -702,7 +703,7 @@ struct Bar2Template : public BarTemplates {
702703
// CHECK-CC46-NEXT: Objective-C interface
703704

704705
// RUN: c-index-test -code-completion-at=%s:84:12 %s | FileCheck -check-prefix=CHECK-CC47 %s
705-
// CHECK-CC47: OverloadCandidate:{ResultType void}{Text foo_12}{LeftParen (}{CurrentParameter int}{Comma , }{Placeholder int}{RightParen )} (1)
706+
// CHECK-CC47: OverloadCandidate:{ResultType void}{Text foo_12}{LeftParen (}{CurrentParameter T}{Comma , }{Placeholder T}{RightParen )} (1)
706707
// CHECK-CC47: Completion contexts:
707708
// CHECK-CC47-NEXT: Any type
708709
// CHECK-CC47-NEXT: Any value

clang/test/SemaCXX/cxx2b-deducing-this.cpp

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,49 @@ static_assert(__is_same(X<B{0}>, X<B{0}>));
10121012
static_assert(!__is_same(X<B{0}>, X<B{1}>));
10131013
} // namespace defaulted_compare
10141014

1015+
namespace static_overloaded_operator {
1016+
struct A {
1017+
template<auto N>
1018+
static void operator()(const char (&)[N]);
1019+
void operator()(this auto &&, auto &&);
1020+
1021+
void implicit_this() {
1022+
operator()("123");
1023+
}
1024+
};
1025+
1026+
struct B {
1027+
template<auto N>
1028+
void operator()(this auto &&, const char (&)[N]);
1029+
static void operator()(auto &&);
1030+
1031+
void implicit_this() {
1032+
operator()("123");
1033+
}
1034+
};
1035+
1036+
struct C {
1037+
template<auto N>
1038+
static void operator[](const char (&)[N]);
1039+
void operator[](this auto &&, auto &&);
1040+
1041+
void implicit_this() {
1042+
operator[]("123");
1043+
}
1044+
};
1045+
1046+
struct D {
1047+
template<auto N>
1048+
void operator[](this auto &&, const char (&)[N]);
1049+
static void operator[](auto &&);
1050+
1051+
void implicit_this() {
1052+
operator[]("123");
1053+
}
1054+
};
1055+
1056+
} // namespace static_overloaded_operator
1057+
10151058
namespace GH102025 {
10161059
struct Foo {
10171060
template <class T>

clang/test/SemaTemplate/GH18291.cpp

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// RUN: %clang_cc1 -std=c++23 -verify %s
2+
3+
namespace t1 {
4+
template<bool> struct enable_if { typedef void type; };
5+
template <class T> class Foo {};
6+
template <class X> constexpr bool check() { return true; }
7+
template <class X, class Enable = void> struct Bar {};
8+
9+
template<class X> void func(Bar<X, typename enable_if<check<X>()>::type>) {}
10+
// expected-note@-1 {{candidate function}}
11+
12+
template<class T> void func(Bar<Foo<T>>) {}
13+
// expected-note@-1 {{candidate function}}
14+
15+
void g() {
16+
func(Bar<Foo<int>>()); // expected-error {{call to 'func' is ambiguous}}
17+
}
18+
} // namespace t1
19+
20+
namespace t2 {
21+
template <bool> struct enable_if;
22+
template <> struct enable_if<true> {
23+
typedef int type;
24+
};
25+
struct pair {
26+
template <int = 0> pair(int);
27+
template <class _U2, enable_if<__is_constructible(int &, _U2)>::type = 0>
28+
pair(_U2 &&);
29+
};
30+
int test_test_i;
31+
void test() { pair{test_test_i}; }
32+
} // namespace t2

clang/test/SemaTemplate/cwg2398.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,20 @@ namespace class_template {
7474
// new-error@-1 {{ambiguous partial specialization}}
7575
} // namespace class_template
7676

77+
namespace class_template_func {
78+
template <class T1, class T2 = float> struct A {};
79+
80+
template <template <class T4> class TT1, class T5> void f(TT1<T5>);
81+
// new-note@-1 {{candidate function}}
82+
83+
template <class T6, class T7> void f(A<T6, T7>) {};
84+
// new-note@-1 {{candidate function}}
85+
86+
void g() {
87+
f(A<int>()); // new-error {{call to 'f' is ambiguous}}
88+
}
89+
} // namespace class_template_func
90+
7791
namespace type_pack1 {
7892
template<class T2> struct A;
7993
template<template<class ...T3s> class TT1, class T4> struct A<TT1<T4>> ;

clang/test/SemaTemplate/temp_arg_nontype.cpp

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -458,17 +458,13 @@ namespace dependent_nested_partial_specialization {
458458
namespace nondependent_default_arg_ordering {
459459
int n, m;
460460
template<typename A, A B = &n> struct X {};
461-
template<typename A> void f(X<A>); // expected-note {{candidate}}
462-
template<typename A> void f(X<A, &m>); // expected-note {{candidate}}
463-
template<typename A, A B> void f(X<A, B>); // expected-note 2{{candidate}}
461+
template<typename A> void f(X<A>);
462+
template<typename A> void f(X<A, &m>);
463+
template<typename A, A B> void f(X<A, B>);
464464
template<template<typename U, U> class T, typename A, int *B> void f(T<A, B>);
465465
void g() {
466-
// FIXME: The first and second function templates above should be
467-
// considered more specialized than the third, but during partial
468-
// ordering we fail to check that we actually deduced template arguments
469-
// that make the deduced A identical to A.
470-
X<int *, &n> x; f(x); // expected-error {{ambiguous}}
471-
X<int *, &m> y; f(y); // expected-error {{ambiguous}}
466+
X<int *, &n> x; f(x);
467+
X<int *, &m> y; f(y);
472468
}
473469
}
474470

clang/test/SemaTemplate/temp_arg_type.cpp

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,10 @@ namespace deduce_noexcept {
6969
void noexcept_function() noexcept;
7070
void throwing_function();
7171

72-
template<typename T, bool B> float &deduce_function(T(*)() noexcept(B)); // expected-note {{candidate}}
73-
template<typename T> int &deduce_function(T(*)() noexcept); // expected-note {{candidate}}
72+
template<typename T, bool B> float &deduce_function(T(*)() noexcept(B));
73+
template<typename T> int &deduce_function(T(*)() noexcept);
7474
void test_function_deduction() {
75-
// FIXME: This should probably unambiguously select the second overload.
76-
int &r = deduce_function(noexcept_function); // expected-error {{ambiguous}}
75+
int &r = deduce_function(noexcept_function);
7776
float &s = deduce_function(throwing_function);
7877
}
7978

0 commit comments

Comments
 (0)