Skip to content

Commit 4576a77

Browse files
committed
PR33222: Require the declared return type not the actual return type to
match when checking for redeclaration of a function template. This properly handles differences in deduced return types, particularly when performing redeclaration checks for a friend function template. llvm-svn: 341778
1 parent 4d10ba3 commit 4576a77

File tree

5 files changed

+88
-13
lines changed

5 files changed

+88
-13
lines changed

clang/include/clang/AST/Decl.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2269,23 +2269,30 @@ class FunctionDecl : public DeclaratorDecl,
22692269
unsigned getMinRequiredArguments() const;
22702270

22712271
QualType getReturnType() const {
2272-
assert(getType()->getAs<FunctionType>() && "Expected a FunctionType!");
2273-
return getType()->getAs<FunctionType>()->getReturnType();
2272+
return getType()->castAs<FunctionType>()->getReturnType();
22742273
}
22752274

22762275
/// Attempt to compute an informative source range covering the
22772276
/// function return type. This may omit qualifiers and other information with
22782277
/// limited representation in the AST.
22792278
SourceRange getReturnTypeSourceRange() const;
22802279

2280+
/// Get the declared return type, which may differ from the actual return
2281+
/// type if the return type is deduced.
2282+
QualType getDeclaredReturnType() const {
2283+
auto *TSI = getTypeSourceInfo();
2284+
QualType T = TSI ? TSI->getType() : getType();
2285+
return T->castAs<FunctionType>()->getReturnType();
2286+
}
2287+
22812288
/// Attempt to compute an informative source range covering the
22822289
/// function exception specification, if any.
22832290
SourceRange getExceptionSpecSourceRange() const;
22842291

22852292
/// Determine the type of an expression that calls this function.
22862293
QualType getCallResultType() const {
2287-
assert(getType()->getAs<FunctionType>() && "Expected a FunctionType!");
2288-
return getType()->getAs<FunctionType>()->getCallResultType(getASTContext());
2294+
return getType()->castAs<FunctionType>()->getCallResultType(
2295+
getASTContext());
22892296
}
22902297

22912298
/// Returns the WarnUnusedResultAttr that is either declared on this

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3245,20 +3245,15 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD,
32453245
// Redeclarations or specializations of a function or function template
32463246
// with a declared return type that uses a placeholder type shall also
32473247
// use that placeholder, not a deduced type.
3248-
QualType OldDeclaredReturnType =
3249-
(Old->getTypeSourceInfo()
3250-
? Old->getTypeSourceInfo()->getType()->castAs<FunctionType>()
3251-
: OldType)->getReturnType();
3252-
QualType NewDeclaredReturnType =
3253-
(New->getTypeSourceInfo()
3254-
? New->getTypeSourceInfo()->getType()->castAs<FunctionType>()
3255-
: NewType)->getReturnType();
3248+
QualType OldDeclaredReturnType = Old->getDeclaredReturnType();
3249+
QualType NewDeclaredReturnType = New->getDeclaredReturnType();
32563250
if (!Context.hasSameType(OldDeclaredReturnType, NewDeclaredReturnType) &&
32573251
canFullyTypeCheckRedeclaration(New, Old, NewDeclaredReturnType,
32583252
OldDeclaredReturnType)) {
32593253
QualType ResQT;
32603254
if (NewDeclaredReturnType->isObjCObjectPointerType() &&
32613255
OldDeclaredReturnType->isObjCObjectPointerType())
3256+
// FIXME: This does the wrong thing for a deduced return type.
32623257
ResQT = Context.mergeObjCGCQualifiers(NewQType, OldQType);
32633258
if (ResQT.isNull()) {
32643259
if (New->isCXXClassMember() && New->isOutOfLine())

clang/lib/Sema/SemaOverload.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1105,7 +1105,8 @@ bool Sema::IsOverload(FunctionDecl *New, FunctionDecl *Old,
11051105
(!TemplateParameterListsAreEqual(NewTemplate->getTemplateParameters(),
11061106
OldTemplate->getTemplateParameters(),
11071107
false, TPL_TemplateMatch) ||
1108-
OldType->getReturnType() != NewType->getReturnType()))
1108+
!Context.hasSameType(Old->getDeclaredReturnType(),
1109+
New->getDeclaredReturnType())))
11091110
return true;
11101111

11111112
// If the function is a class member, its signature includes the

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8294,6 +8294,8 @@ Sema::CheckMemberSpecialization(NamedDecl *Member, LookupResult &Previous) {
82948294
QualType Adjusted = Function->getType();
82958295
if (!hasExplicitCallingConv(Adjusted))
82968296
Adjusted = adjustCCAndNoReturn(Adjusted, Method->getType());
8297+
// This doesn't handle deduced return types, but both function
8298+
// declarations should be undeduced at this point.
82978299
if (Context.hasSameType(Adjusted, Method->getType())) {
82988300
FoundInstantiation = *I;
82998301
Instantiation = Method;

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

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -552,3 +552,73 @@ namespace PR24989 {
552552
void forinit_decltypeauto() {
553553
for (decltype(auto) forinit_decltypeauto_inner();;) {} // expected-warning {{interpreted as a function}} expected-note {{replace}}
554554
}
555+
556+
namespace PR33222 {
557+
auto f1();
558+
auto f2();
559+
560+
template<typename T> decltype(auto) g0(T x) { return x.n; }
561+
template<typename T> decltype(auto) g1(T);
562+
template<typename T> decltype(auto) g2(T);
563+
564+
struct X {
565+
static auto f1();
566+
static auto f2();
567+
568+
template<typename T> static decltype(auto) g0(T x) { return x.n; } // FIXME (PR38883): expected-error {{private}}
569+
template<typename T> static decltype(auto) g1(T);
570+
template<typename T> static decltype(auto) g2(T);
571+
};
572+
573+
template<typename U> class A {
574+
friend auto f1();
575+
friend auto f2();
576+
577+
// FIXME (PR38883): This friend declaration doesn't actually work, because
578+
// we fail to look up the named function properly during instantiation.
579+
friend decltype(auto) g0<>(A);
580+
template<typename T> friend decltype(auto) g1(T);
581+
template<typename T> friend decltype(auto) g2(T);
582+
583+
friend auto X::f1();
584+
friend auto X::f2();
585+
586+
// FIXME (PR38882): 'A' names the class template not the injected-class-name here!
587+
friend decltype(auto) X::g0<>(A<U>);
588+
// FIXME (PR38882): ::T hides the template parameter if both are named T here!
589+
template<typename T_> friend decltype(auto) X::g1(T_);
590+
template<typename T_> friend decltype(auto) X::g2(T_);
591+
592+
int n; // FIXME: expected-note {{here}}
593+
};
594+
595+
auto f1() { return A<int>().n; }
596+
template<typename T> decltype(auto) g1(T x) { return A<int>().n; }
597+
598+
auto X::f1() { return A<int>().n; }
599+
template<typename T> decltype(auto) X::g1(T x) { return A<int>().n; }
600+
601+
A<int> ai;
602+
int k1 = g0(ai);
603+
int k2 = X::g0(ai); // FIXME: expected-note {{in instantiation of}}
604+
605+
int k3 = g1(ai);
606+
int k4 = X::g1(ai);
607+
608+
auto f2() { return A<int>().n; }
609+
template<typename T> decltype(auto) g2(T x) { return A<int>().n; }
610+
611+
auto X::f2() { return A<int>().n; }
612+
template<typename T> decltype(auto) X::g2(T x) { return A<int>().n; }
613+
614+
int k5 = g2(ai);
615+
int k6 = X::g2(ai);
616+
617+
template<typename> struct B {
618+
auto *q() { return (float*)0; } // expected-note 2{{previous}}
619+
};
620+
template<> auto *B<char[1]>::q() { return (int*)0; }
621+
template<> auto B<char[2]>::q() { return (int*)0; } // expected-error {{return type}}
622+
// FIXME: suppress this follow-on error: expected-error@-1 {{cannot initialize}}
623+
template<> int B<char[3]>::q() { return 0; } // expected-error {{return type}}
624+
}

0 commit comments

Comments
 (0)