Skip to content

Commit 89e5db4

Browse files
committed
[clang] check deduction consistency when partial ordering function templates
This makes partial ordering of function templates consistent with other entities. Fixes #18291
1 parent 70a9535 commit 89e5db4

12 files changed

+727
-306
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,8 @@ Bug Fixes to C++ Support
153153

154154
- Fixed a crash when an expression with a dependent ``__typeof__`` type is used as the operand of a unary operator. (#GH97646)
155155
- Fixed a failed assertion when checking invalid delete operator declaration. (#GH96191)
156+
- When performing partial ordering of function templates, clang now checks that
157+
the deduction was consistent. Fixes (#GH18291).
156158

157159
Bug Fixes to AST Handling
158160
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13255,6 +13255,10 @@ class Sema final : public SemaBase {
1325513255
/// \param AllowDeducedTST Whether a DeducedTemplateSpecializationType is
1325613256
/// acceptable as the top level type of the result.
1325713257
///
13258+
/// \param IsIncompleteSubstitution If provided, the pointee will be set
13259+
/// whenever substitution would perform a replacement with a null or
13260+
/// non-existent template argument.
13261+
///
1325813262
/// \returns If the instantiation succeeds, the instantiated
1325913263
/// type. Otherwise, produces diagnostics and returns a NULL type.
1326013264
TypeSourceInfo *SubstType(TypeSourceInfo *T,
@@ -13264,7 +13268,8 @@ class Sema final : public SemaBase {
1326413268

1326513269
QualType SubstType(QualType T,
1326613270
const MultiLevelTemplateArgumentList &TemplateArgs,
13267-
SourceLocation Loc, DeclarationName Entity);
13271+
SourceLocation Loc, DeclarationName Entity,
13272+
bool *IsIncompleteSubstitution = nullptr);
1326813273

1326913274
TypeSourceInfo *SubstType(TypeLoc TL,
1327013275
const MultiLevelTemplateArgumentList &TemplateArgs,

clang/lib/AST/ExprConstant.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5346,7 +5346,6 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
53465346
const Expr *RetExpr = cast<ReturnStmt>(S)->getRetValue();
53475347
FullExpressionRAII Scope(Info);
53485348
if (RetExpr && RetExpr->isValueDependent()) {
5349-
EvaluateDependentExpr(RetExpr, Info);
53505349
// We know we returned, but we don't know what the value is.
53515350
return ESR_Failed;
53525351
}

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 579 additions & 249 deletions
Large diffs are not rendered by default.

clang/lib/Sema/SemaTemplateInstantiate.cpp

Lines changed: 24 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1342,6 +1342,9 @@ namespace {
13421342
DeclarationName Entity;
13431343
// Whether to evaluate the C++20 constraints or simply substitute into them.
13441344
bool EvaluateConstraints = true;
1345+
// Whether Substitution was Incomplete, that is, we tried to substitute in
1346+
// any template arguments which were null.
1347+
bool IsIncomplete = false;
13451348

13461349
public:
13471350
typedef TreeTransform<TemplateInstantiator> inherited;
@@ -1372,6 +1375,9 @@ namespace {
13721375
/// Returns the name of the entity being instantiated, if any.
13731376
DeclarationName getBaseEntity() { return Entity; }
13741377

1378+
/// Returns whether any substitution so far was incomplete.
1379+
bool getIsIncomplete() const { return IsIncomplete; }
1380+
13751381
/// Sets the "base" location and entity when that
13761382
/// information is known based on another transformation.
13771383
void setBase(SourceLocation Loc, DeclarationName Entity) {
@@ -1418,6 +1424,8 @@ namespace {
14181424
if (TemplateArgs.hasTemplateArgument(Depth, Index)) {
14191425
Result = TemplateArgs(Depth, Index);
14201426
TemplateArgs.setArgument(Depth, Index, TemplateArgument());
1427+
} else {
1428+
IsIncomplete = true;
14211429
}
14221430
}
14231431

@@ -1815,8 +1823,10 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
18151823
// template arguments in a function template, but there were some
18161824
// arguments left unspecified.
18171825
if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
1818-
TTP->getPosition()))
1826+
TTP->getPosition())) {
1827+
IsIncomplete = true;
18191828
return D;
1829+
}
18201830

18211831
TemplateArgument Arg = TemplateArgs(TTP->getDepth(), TTP->getPosition());
18221832

@@ -1962,8 +1972,10 @@ TemplateName TemplateInstantiator::TransformTemplateName(
19621972
// template arguments in a function template, but there were some
19631973
// arguments left unspecified.
19641974
if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
1965-
TTP->getPosition()))
1975+
TTP->getPosition())) {
1976+
IsIncomplete = true;
19661977
return Name;
1978+
}
19671979

19681980
TemplateArgument Arg = TemplateArgs(TTP->getDepth(), TTP->getPosition());
19691981

@@ -2045,8 +2057,10 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
20452057
// template arguments in a function template, but there were some
20462058
// arguments left unspecified.
20472059
if (!TemplateArgs.hasTemplateArgument(NTTP->getDepth(),
2048-
NTTP->getPosition()))
2060+
NTTP->getPosition())) {
2061+
IsIncomplete = true;
20492062
return E;
2063+
}
20502064

20512065
TemplateArgument Arg = TemplateArgs(NTTP->getDepth(), NTTP->getPosition());
20522066

@@ -2465,6 +2479,7 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
24652479
// template arguments in a function template class, but there were some
24662480
// arguments left unspecified.
24672481
if (!TemplateArgs.hasTemplateArgument(T->getDepth(), T->getIndex())) {
2482+
IsIncomplete = true;
24682483
TemplateTypeParmTypeLoc NewTL
24692484
= TLB.push<TemplateTypeParmTypeLoc>(TL.getType());
24702485
NewTL.setNameLoc(TL.getNameLoc());
@@ -2836,7 +2851,8 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL,
28362851
/// Deprecated form of the above.
28372852
QualType Sema::SubstType(QualType T,
28382853
const MultiLevelTemplateArgumentList &TemplateArgs,
2839-
SourceLocation Loc, DeclarationName Entity) {
2854+
SourceLocation Loc, DeclarationName Entity,
2855+
bool *IsIncompleteSubstitution) {
28402856
assert(!CodeSynthesisContexts.empty() &&
28412857
"Cannot perform an instantiation without some context on the "
28422858
"instantiation stack");
@@ -2847,7 +2863,10 @@ QualType Sema::SubstType(QualType T,
28472863
return T;
28482864

28492865
TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, Entity);
2850-
return Instantiator.TransformType(T);
2866+
QualType QT = Instantiator.TransformType(T);
2867+
if (IsIncompleteSubstitution && Instantiator.getIsIncomplete())
2868+
*IsIncompleteSubstitution = true;
2869+
return QT;
28512870
}
28522871

28532872
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/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)