Skip to content

Commit 14b0812

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 52bfb26 commit 14b0812

12 files changed

+726
-306
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,8 @@ Bug Fixes to C++ Support
276276
- Clang now properly handles the order of attributes in `extern` blocks. (#GH101990).
277277
- Fixed an assertion failure by preventing null explicit object arguments from being deduced. (#GH102025).
278278
- Correctly check constraints of explicit instantiations of member functions. (#GH46029)
279+
- When performing partial ordering of function templates, clang now checks that
280+
the deduction was consistent. Fixes (#GH18291).
279281

280282
Bug Fixes to AST Handling
281283
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13273,6 +13273,10 @@ class Sema final : public SemaBase {
1327313273
/// \param AllowDeducedTST Whether a DeducedTemplateSpecializationType is
1327413274
/// acceptable as the top level type of the result.
1327513275
///
13276+
/// \param IsIncompleteSubstitution If provided, the pointee will be set
13277+
/// whenever substitution would perform a replacement with a null or
13278+
/// non-existent template argument.
13279+
///
1327613280
/// \returns If the instantiation succeeds, the instantiated
1327713281
/// type. Otherwise, produces diagnostics and returns a NULL type.
1327813282
TypeSourceInfo *SubstType(TypeSourceInfo *T,
@@ -13282,7 +13286,8 @@ class Sema final : public SemaBase {
1328213286

1328313287
QualType SubstType(QualType T,
1328413288
const MultiLevelTemplateArgumentList &TemplateArgs,
13285-
SourceLocation Loc, DeclarationName Entity);
13289+
SourceLocation Loc, DeclarationName Entity,
13290+
bool *IsIncompleteSubstitution = nullptr);
1328613291

1328713292
TypeSourceInfo *SubstType(TypeLoc TL,
1328813293
const MultiLevelTemplateArgumentList &TemplateArgs,

clang/lib/AST/ExprConstant.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5399,7 +5399,6 @@ static EvalStmtResult EvaluateStmt(StmtResult &Result, EvalInfo &Info,
53995399
const Expr *RetExpr = cast<ReturnStmt>(S)->getRetValue();
54005400
FullExpressionRAII Scope(Info);
54015401
if (RetExpr && RetExpr->isValueDependent()) {
5402-
EvaluateDependentExpr(RetExpr, Info);
54035402
// We know we returned, but we don't know what the value is.
54045403
return ESR_Failed;
54055404
}

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 578 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
@@ -1343,6 +1343,9 @@ namespace {
13431343
DeclarationName Entity;
13441344
// Whether to evaluate the C++20 constraints or simply substitute into them.
13451345
bool EvaluateConstraints = true;
1346+
// Whether Substitution was Incomplete, that is, we tried to substitute in
1347+
// any template arguments which were null.
1348+
bool IsIncomplete = false;
13461349

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

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

@@ -1814,8 +1822,10 @@ Decl *TemplateInstantiator::TransformDecl(SourceLocation Loc, Decl *D) {
18141822
// template arguments in a function template, but there were some
18151823
// arguments left unspecified.
18161824
if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
1817-
TTP->getPosition()))
1825+
TTP->getPosition())) {
1826+
IsIncomplete = true;
18181827
return D;
1828+
}
18191829

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

@@ -1961,8 +1971,10 @@ TemplateName TemplateInstantiator::TransformTemplateName(
19611971
// template arguments in a function template, but there were some
19621972
// arguments left unspecified.
19631973
if (!TemplateArgs.hasTemplateArgument(TTP->getDepth(),
1964-
TTP->getPosition()))
1974+
TTP->getPosition())) {
1975+
IsIncomplete = true;
19651976
return Name;
1977+
}
19661978

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

@@ -2044,8 +2056,10 @@ TemplateInstantiator::TransformTemplateParmRefExpr(DeclRefExpr *E,
20442056
// template arguments in a function template, but there were some
20452057
// arguments left unspecified.
20462058
if (!TemplateArgs.hasTemplateArgument(NTTP->getDepth(),
2047-
NTTP->getPosition()))
2059+
NTTP->getPosition())) {
2060+
IsIncomplete = true;
20482061
return E;
2062+
}
20492063

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

@@ -2464,6 +2478,7 @@ TemplateInstantiator::TransformTemplateTypeParmType(TypeLocBuilder &TLB,
24642478
// template arguments in a function template class, but there were some
24652479
// arguments left unspecified.
24662480
if (!TemplateArgs.hasTemplateArgument(T->getDepth(), T->getIndex())) {
2481+
IsIncomplete = true;
24672482
TemplateTypeParmTypeLoc NewTL
24682483
= TLB.push<TemplateTypeParmTypeLoc>(TL.getType());
24692484
NewTL.setNameLoc(TL.getNameLoc());
@@ -2835,7 +2850,8 @@ TypeSourceInfo *Sema::SubstType(TypeLoc TL,
28352850
/// Deprecated form of the above.
28362851
QualType Sema::SubstType(QualType T,
28372852
const MultiLevelTemplateArgumentList &TemplateArgs,
2838-
SourceLocation Loc, DeclarationName Entity) {
2853+
SourceLocation Loc, DeclarationName Entity,
2854+
bool *IsIncompleteSubstitution) {
28392855
assert(!CodeSynthesisContexts.empty() &&
28402856
"Cannot perform an instantiation without some context on the "
28412857
"instantiation stack");
@@ -2846,7 +2862,10 @@ QualType Sema::SubstType(QualType T,
28462862
return T;
28472863

28482864
TemplateInstantiator Instantiator(*this, TemplateArgs, Loc, Entity);
2849-
return Instantiator.TransformType(T);
2865+
QualType QT = Instantiator.TransformType(T);
2866+
if (IsIncompleteSubstitution && Instantiator.getIsIncomplete())
2867+
*IsIncompleteSubstitution = true;
2868+
return QT;
28502869
}
28512870

28522871
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)