Skip to content

Commit eb08c0f

Browse files
authored
[Clang][Sema] Fix explicit specializations of member function templates with a deduced return type (#86817)
Clang erroneously rejects the following: ``` template<typename T> struct A { template<typename U> auto f(); }; template<> template<typename U> auto A<int>::f(); // error: conflicting types for 'f' ``` This happens because the explicit specialization of `f` has its return type replaced with a dependent `AutoType` in `ActOnFunctionDeclarator`, but no such replacement occurs for the implicitly instantiated function template `A<int>::f`. Since the return types don't match, the explicit specialization is diagnosed as an invalid redeclaration. This patch moves the replacement of the return type to `CheckFunctionDeclaration` so it also happens during instantiation. `setObjectOfFriendDecl` will have been called by then, so the `isFriend && CurContext->isDependentContext()` condition is made redundant & removed (as it already happens in `DeclContext::isDependentContext`). `Sema::IsOverload` only checks the _declared_ return type (which isn't changed by the adjustment), so adjusting the return type afterwards should be safe.
1 parent a4798bb commit eb08c0f

File tree

4 files changed

+50
-20
lines changed

4 files changed

+50
-20
lines changed

clang-tools-extra/test/clang-tidy/infrastructure/diagnostic.cpp

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
// RUN: not clang-tidy -checks='-*,modernize-use-override' %T/diagnostics/input.cpp -- -DCOMPILATION_ERROR 2>&1 | FileCheck -check-prefix=CHECK6 -implicit-check-not='{{warning:|error:}}' %s
2626
// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined' %s -- -DMACRO_FROM_COMMAND_LINE -std=c++20 | FileCheck -check-prefix=CHECK4 -implicit-check-not='{{warning:|error:}}' %s
2727
// RUN: clang-tidy -checks='-*,modernize-use-override,clang-diagnostic-macro-redefined,clang-diagnostic-literal-conversion' %s -- -DMACRO_FROM_COMMAND_LINE -std=c++20 -Wno-macro-redefined | FileCheck --check-prefix=CHECK7 -implicit-check-not='{{warning:|error:}}' %s
28-
// RUN: not clang-tidy -checks='-*,modernize-use-override' %s -- -std=c++20 -DPR64602 | FileCheck -check-prefix=CHECK8 -implicit-check-not='{{warning:|error:}}' %s
28+
// RUN: clang-tidy -checks='-*,modernize-use-override' %s -- -std=c++20 -DPR64602
2929

3030
// CHECK1: error: no input files [clang-diagnostic-error]
3131
// CHECK1: error: no such file or directory: '{{.*}}nonexistent.cpp' [clang-diagnostic-error]
@@ -68,6 +68,4 @@ auto S<>::foo(auto)
6868
{
6969
return 1;
7070
}
71-
// CHECK8: error: conflicting types for 'foo' [clang-diagnostic-error]
72-
// CHECK8: note: previous declaration is here
7371
#endif

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -483,6 +483,8 @@ Bug Fixes to C++ Support
483483
following the first `::` were ignored).
484484
- Fix an out-of-bounds crash when checking the validity of template partial specializations. (part of #GH86757).
485485
- Fix an issue caused by not handling invalid cases when substituting into the parameter mapping of a constraint. Fixes (#GH86757).
486+
- Fixed a bug that prevented member function templates of class templates declared with a deduced return type
487+
from being explicitly specialized for a given implicit instantiation of the class template.
486488

487489
Bug Fixes to AST Handling
488490
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/lib/Sema/SemaDecl.cpp

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10124,23 +10124,6 @@ Sema::ActOnFunctionDeclarator(Scope *S, Declarator &D, DeclContext *DC,
1012410124
Diag(D.getDeclSpec().getVirtualSpecLoc(), diag::err_auto_fn_virtual);
1012510125
}
1012610126

10127-
if (getLangOpts().CPlusPlus14 &&
10128-
(NewFD->isDependentContext() ||
10129-
(isFriend && CurContext->isDependentContext())) &&
10130-
NewFD->getReturnType()->isUndeducedType()) {
10131-
// If the function template is referenced directly (for instance, as a
10132-
// member of the current instantiation), pretend it has a dependent type.
10133-
// This is not really justified by the standard, but is the only sane
10134-
// thing to do.
10135-
// FIXME: For a friend function, we have not marked the function as being
10136-
// a friend yet, so 'isDependentContext' on the FD doesn't work.
10137-
const FunctionProtoType *FPT =
10138-
NewFD->getType()->castAs<FunctionProtoType>();
10139-
QualType Result = SubstAutoTypeDependent(FPT->getReturnType());
10140-
NewFD->setType(Context.getFunctionType(Result, FPT->getParamTypes(),
10141-
FPT->getExtProtoInfo()));
10142-
}
10143-
1014410127
// C++ [dcl.fct.spec]p3:
1014510128
// The inline specifier shall not appear on a block scope function
1014610129
// declaration.
@@ -12112,6 +12095,35 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,
1211212095

1211312096
CheckConstPureAttributesUsage(*this, NewFD);
1211412097

12098+
// C++ [dcl.spec.auto.general]p12:
12099+
// Return type deduction for a templated function with a placeholder in its
12100+
// declared type occurs when the definition is instantiated even if the
12101+
// function body contains a return statement with a non-type-dependent
12102+
// operand.
12103+
//
12104+
// C++ [temp.dep.expr]p3:
12105+
// An id-expression is type-dependent if it is a template-id that is not a
12106+
// concept-id and is dependent; or if its terminal name is:
12107+
// - [...]
12108+
// - associated by name lookup with one or more declarations of member
12109+
// functions of a class that is the current instantiation declared with a
12110+
// return type that contains a placeholder type,
12111+
// - [...]
12112+
//
12113+
// If this is a templated function with a placeholder in its return type,
12114+
// make the placeholder type dependent since it won't be deduced until the
12115+
// definition is instantiated. We do this here because it needs to happen
12116+
// for implicitly instantiated member functions/member function templates.
12117+
if (getLangOpts().CPlusPlus14 &&
12118+
(NewFD->isDependentContext() &&
12119+
NewFD->getReturnType()->isUndeducedType())) {
12120+
const FunctionProtoType *FPT =
12121+
NewFD->getType()->castAs<FunctionProtoType>();
12122+
QualType NewReturnType = SubstAutoTypeDependent(FPT->getReturnType());
12123+
NewFD->setType(Context.getFunctionType(NewReturnType, FPT->getParamTypes(),
12124+
FPT->getExtProtoInfo()));
12125+
}
12126+
1211512127
// C++11 [dcl.constexpr]p8:
1211612128
// A constexpr specifier for a non-static member function that is not
1211712129
// a constructor declares that member function to be const.

clang/test/SemaCXX/deduced-return-type-cxx14.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,24 @@ namespace Templates {
237237
int (S::*(*p)())(double) = f;
238238
int (S::*(*q)())(double) = f<S, double>;
239239
}
240+
241+
template<typename T>
242+
struct MemberSpecialization {
243+
auto f();
244+
template<typename U> auto f(U);
245+
template<typename U> auto *f(U);
246+
};
247+
248+
template<>
249+
auto MemberSpecialization<int>::f();
250+
251+
template<>
252+
template<typename U>
253+
auto MemberSpecialization<int>::f(U);
254+
255+
template<>
256+
template<typename U>
257+
auto *MemberSpecialization<int>::f(U);
240258
}
241259

242260
auto fwd_decl_using();

0 commit comments

Comments
 (0)