Skip to content

Commit c58bc24

Browse files
authored
[Clang] Only compare template params of potential overload after checking their decl context (#78139)
Fixes a regression from 69066ab in which we compared the template lists of potential overloads before checkings their declaration contexts. This would cause a crash when doing constraint substitution as part of that template check, because we would try to refer to not yet instantiated entities (the underlying cause is unclear). This patch reorders (again) when we look at template parameter so we don't do it when checkings friends in different lexical contexts. Fixes #77953 Fixes #78101
1 parent 10602c2 commit c58bc24

File tree

3 files changed

+89
-28
lines changed

3 files changed

+89
-28
lines changed

clang/lib/Sema/SemaOverload.cpp

+34-28
Original file line numberDiff line numberDiff line change
@@ -1259,6 +1259,40 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
12591259
if ((OldTemplate == nullptr) != (NewTemplate == nullptr))
12601260
return true;
12611261

1262+
// Is the function New an overload of the function Old?
1263+
QualType OldQType = SemaRef.Context.getCanonicalType(Old->getType());
1264+
QualType NewQType = SemaRef.Context.getCanonicalType(New->getType());
1265+
1266+
// Compare the signatures (C++ 1.3.10) of the two functions to
1267+
// determine whether they are overloads. If we find any mismatch
1268+
// in the signature, they are overloads.
1269+
1270+
// If either of these functions is a K&R-style function (no
1271+
// prototype), then we consider them to have matching signatures.
1272+
if (isa<FunctionNoProtoType>(OldQType.getTypePtr()) ||
1273+
isa<FunctionNoProtoType>(NewQType.getTypePtr()))
1274+
return false;
1275+
1276+
const auto *OldType = cast<FunctionProtoType>(OldQType);
1277+
const auto *NewType = cast<FunctionProtoType>(NewQType);
1278+
1279+
// The signature of a function includes the types of its
1280+
// parameters (C++ 1.3.10), which includes the presence or absence
1281+
// of the ellipsis; see C++ DR 357).
1282+
if (OldQType != NewQType && OldType->isVariadic() != NewType->isVariadic())
1283+
return true;
1284+
1285+
// For member-like friends, the enclosing class is part of the signature.
1286+
if ((New->isMemberLikeConstrainedFriend() ||
1287+
Old->isMemberLikeConstrainedFriend()) &&
1288+
!New->getLexicalDeclContext()->Equals(Old->getLexicalDeclContext()))
1289+
return true;
1290+
1291+
// Compare the parameter lists.
1292+
// This can only be done once we have establish that friend functions
1293+
// inhabit the same context, otherwise we might tried to instantiate
1294+
// references to non-instantiated entities during constraint substitution.
1295+
// GH78101.
12621296
if (NewTemplate) {
12631297
// C++ [temp.over.link]p4:
12641298
// The signature of a function template consists of its function
@@ -1296,34 +1330,6 @@ static bool IsOverloadOrOverrideImpl(Sema &SemaRef, FunctionDecl *New,
12961330
return true;
12971331
}
12981332

1299-
// Is the function New an overload of the function Old?
1300-
QualType OldQType = SemaRef.Context.getCanonicalType(Old->getType());
1301-
QualType NewQType = SemaRef.Context.getCanonicalType(New->getType());
1302-
1303-
// Compare the signatures (C++ 1.3.10) of the two functions to
1304-
// determine whether they are overloads. If we find any mismatch
1305-
// in the signature, they are overloads.
1306-
1307-
// If either of these functions is a K&R-style function (no
1308-
// prototype), then we consider them to have matching signatures.
1309-
if (isa<FunctionNoProtoType>(OldQType.getTypePtr()) ||
1310-
isa<FunctionNoProtoType>(NewQType.getTypePtr()))
1311-
return false;
1312-
1313-
const FunctionProtoType *OldType = cast<FunctionProtoType>(OldQType);
1314-
const FunctionProtoType *NewType = cast<FunctionProtoType>(NewQType);
1315-
1316-
// The signature of a function includes the types of its
1317-
// parameters (C++ 1.3.10), which includes the presence or absence
1318-
// of the ellipsis; see C++ DR 357).
1319-
if (OldQType != NewQType && OldType->isVariadic() != NewType->isVariadic())
1320-
return true;
1321-
1322-
// For member-like friends, the enclosing class is part of the signature.
1323-
if ((New->isMemberLikeConstrainedFriend() ||
1324-
Old->isMemberLikeConstrainedFriend()) &&
1325-
!New->getLexicalDeclContext()->Equals(Old->getLexicalDeclContext()))
1326-
return true;
13271333
const auto *OldMethod = dyn_cast<CXXMethodDecl>(Old);
13281334
const auto *NewMethod = dyn_cast<CXXMethodDecl>(New);
13291335

clang/test/CXX/over/over.load/p2-0x.cpp

+27
Original file line numberDiff line numberDiff line change
@@ -55,4 +55,31 @@ static_assert(not test<type<2>&>);
5555
static_assert(test<type<2>&&>);
5656

5757
}
58+
59+
namespace GH78101 {
60+
61+
template<typename T, typename U, int i>
62+
concept True = true;
63+
64+
template<typename T, int I>
65+
struct Template {
66+
static constexpr int i = I;
67+
friend constexpr auto operator+(True<T, i> auto f) {
68+
return i;
69+
}
70+
};
71+
72+
template<int I>
73+
struct Template<float, I> {
74+
static constexpr int i = I;
75+
friend constexpr auto operator+(True<float, i> auto f) {
76+
return i;
77+
}
78+
};
79+
80+
Template<void, 4> f{};
81+
static_assert(+Template<float, 5>{} == 5);
82+
83+
}
84+
5885
#endif

clang/test/Modules/GH77953.cpp

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// From https://github.com/llvm/llvm-project/issues/77953
2+
// RUN: rm -rf %t
3+
// RUN: mkdir -p %t
4+
// RUN: split-file %s %t
5+
6+
// RUN: %clang_cc1 -std=c++20 -emit-module-interface %t/a.cppm -o %t/a.pcm
7+
// RUN: %clang_cc1 -std=c++20 -fmodule-file=a=%t/a.pcm %t/b.cppm
8+
9+
//--- a.cppm
10+
export module a;
11+
12+
template<typename, typename>
13+
concept c = true;
14+
15+
export template<typename... Ts>
16+
struct a {
17+
template<typename... Us> requires(... and c<Ts, Us>)
18+
friend bool operator==(a, a<Us...>) {
19+
return true;
20+
}
21+
};
22+
23+
template struct a<>;
24+
25+
//--- b.cppm
26+
import a;
27+
28+
template struct a<int>;

0 commit comments

Comments
 (0)