Skip to content

Commit eb389e1

Browse files
committed
Reapply "[Clang][Sema] Fix crash when 'this' is used in a dependent class scope function template specialization that instantiates to a static member function (llvm#87541)"
This patch fixes a crash that happens when '`this`' is referenced (implicitly or explicitly) in a dependent class scope function template specialization that instantiates to a static member function. For example: ``` template<typename T> struct A { template<typename U> static void f(); template<> void f<int>() { this; // causes crash during instantiation } }; template struct A<int>; ``` This happens because during instantiation of the function body, `Sema::getCurrentThisType` will return a null `QualType` which we rebuild the `CXXThisExpr` with. A similar problem exists for implicit class member access expressions in such contexts (which shouldn't really happen within templates anyways per [class.mfct.non.static] p2, but changing that is non-trivial). This patch fixes the crash by building `UnresolvedLookupExpr`s instead of `MemberExpr`s for these implicit member accesses, which will then be correctly rebuilt as `MemberExpr`s during instantiation.
1 parent 54a9f00 commit eb389e1

File tree

8 files changed

+181
-40
lines changed

8 files changed

+181
-40
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -520,6 +520,8 @@ Bug Fixes to C++ Support
520520
- Fix an issue caused by not handling invalid cases when substituting into the parameter mapping of a constraint. Fixes (#GH86757).
521521
- Fixed a bug that prevented member function templates of class templates declared with a deduced return type
522522
from being explicitly specialized for a given implicit instantiation of the class template.
523+
- Fixed a crash when ``this`` is used in a dependent class scope function template specialization
524+
that instantiates to a static member function.
523525

524526
- Fix crash when inheriting from a cv-qualified type. Fixes:
525527
(`#35603 <https://github.com/llvm/llvm-project/issues/35603>`_)

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5439,7 +5439,8 @@ class Sema final : public SemaBase {
54395439

54405440
ExprResult BuildDeclarationNameExpr(const CXXScopeSpec &SS, LookupResult &R,
54415441
bool NeedsADL,
5442-
bool AcceptInvalidDecl = false);
5442+
bool AcceptInvalidDecl = false,
5443+
bool NeedUnresolved = false);
54435444
ExprResult BuildDeclarationNameExpr(
54445445
const CXXScopeSpec &SS, const DeclarationNameInfo &NameInfo, NamedDecl *D,
54455446
NamedDecl *FoundD = nullptr,
@@ -6591,7 +6592,10 @@ class Sema final : public SemaBase {
65916592
SourceLocation RParenLoc);
65926593

65936594
//// ActOnCXXThis - Parse 'this' pointer.
6594-
ExprResult ActOnCXXThis(SourceLocation loc);
6595+
ExprResult ActOnCXXThis(SourceLocation Loc);
6596+
6597+
/// Check whether the type of 'this' is valid in the current context.
6598+
bool CheckCXXThisType(SourceLocation Loc, QualType Type);
65956599

65966600
/// Build a CXXThisExpr and mark it referenced in the current context.
65976601
Expr *BuildCXXThisExpr(SourceLocation Loc, QualType Type, bool IsImplicit);

clang/lib/Sema/SemaExpr.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3442,10 +3442,11 @@ static bool ShouldLookupResultBeMultiVersionOverload(const LookupResult &R) {
34423442

34433443
ExprResult Sema::BuildDeclarationNameExpr(const CXXScopeSpec &SS,
34443444
LookupResult &R, bool NeedsADL,
3445-
bool AcceptInvalidDecl) {
3445+
bool AcceptInvalidDecl,
3446+
bool NeedUnresolved) {
34463447
// If this is a single, fully-resolved result and we don't need ADL,
34473448
// just build an ordinary singleton decl ref.
3448-
if (!NeedsADL && R.isSingleResult() &&
3449+
if (!NeedUnresolved && !NeedsADL && R.isSingleResult() &&
34493450
!R.getAsSingle<FunctionTemplateDecl>() &&
34503451
!ShouldLookupResultBeMultiVersionOverload(R))
34513452
return BuildDeclarationNameExpr(SS, R.getLookupNameInfo(), R.getFoundDecl(),

clang/lib/Sema/SemaExprCXX.cpp

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,26 +1415,42 @@ bool Sema::CheckCXXThisCapture(SourceLocation Loc, const bool Explicit,
14151415
}
14161416

14171417
ExprResult Sema::ActOnCXXThis(SourceLocation Loc) {
1418-
/// C++ 9.3.2: In the body of a non-static member function, the keyword this
1419-
/// is a non-lvalue expression whose value is the address of the object for
1420-
/// which the function is called.
1418+
// C++20 [expr.prim.this]p1:
1419+
// The keyword this names a pointer to the object for which an
1420+
// implicit object member function is invoked or a non-static
1421+
// data member's initializer is evaluated.
14211422
QualType ThisTy = getCurrentThisType();
14221423

1423-
if (ThisTy.isNull()) {
1424-
DeclContext *DC = getFunctionLevelDeclContext();
1424+
if (CheckCXXThisType(Loc, ThisTy))
1425+
return ExprError();
14251426

1426-
if (const auto *Method = dyn_cast<CXXMethodDecl>(DC);
1427-
Method && Method->isExplicitObjectMemberFunction()) {
1428-
return Diag(Loc, diag::err_invalid_this_use) << 1;
1429-
}
1427+
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
1428+
}
14301429

1431-
if (isLambdaCallWithExplicitObjectParameter(CurContext))
1432-
return Diag(Loc, diag::err_invalid_this_use) << 1;
1430+
bool Sema::CheckCXXThisType(SourceLocation Loc, QualType Type) {
1431+
if (!Type.isNull())
1432+
return false;
14331433

1434-
return Diag(Loc, diag::err_invalid_this_use) << 0;
1434+
// C++20 [expr.prim.this]p3:
1435+
// If a declaration declares a member function or member function template
1436+
// of a class X, the expression this is a prvalue of type
1437+
// "pointer to cv-qualifier-seq X" wherever X is the current class between
1438+
// the optional cv-qualifier-seq and the end of the function-definition,
1439+
// member-declarator, or declarator. It shall not appear within the
1440+
// declaration of either a static member function or an explicit object
1441+
// member function of the current class (although its type and value
1442+
// category are defined within such member functions as they are within
1443+
// an implicit object member function).
1444+
DeclContext *DC = getFunctionLevelDeclContext();
1445+
if (const auto *Method = dyn_cast<CXXMethodDecl>(DC);
1446+
Method && Method->isExplicitObjectMemberFunction()) {
1447+
Diag(Loc, diag::err_invalid_this_use) << 1;
1448+
} else if (isLambdaCallWithExplicitObjectParameter(CurContext)) {
1449+
Diag(Loc, diag::err_invalid_this_use) << 1;
1450+
} else {
1451+
Diag(Loc, diag::err_invalid_this_use) << 0;
14351452
}
1436-
1437-
return BuildCXXThisExpr(Loc, ThisTy, /*IsImplicit=*/false);
1453+
return true;
14381454
}
14391455

14401456
Expr *Sema::BuildCXXThisExpr(SourceLocation Loc, QualType Type,

clang/lib/Sema/SemaExprMember.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,10 @@ enum IMAKind {
6161
/// The reference is a contextually-permitted abstract member reference.
6262
IMA_Abstract,
6363

64+
/// Whether the context is static is dependent on the enclosing template (i.e.
65+
/// in a dependent class scope explicit specialization).
66+
IMA_Dependent,
67+
6468
/// The reference may be to an unresolved using declaration and the
6569
/// context is not an instance method.
6670
IMA_Unresolved_StaticOrExplicitContext,
@@ -91,10 +95,18 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
9195

9296
DeclContext *DC = SemaRef.getFunctionLevelDeclContext();
9397

94-
bool isStaticOrExplicitContext =
95-
SemaRef.CXXThisTypeOverride.isNull() &&
96-
(!isa<CXXMethodDecl>(DC) || cast<CXXMethodDecl>(DC)->isStatic() ||
97-
cast<CXXMethodDecl>(DC)->isExplicitObjectMemberFunction());
98+
bool couldInstantiateToStatic = false;
99+
bool isStaticOrExplicitContext = SemaRef.CXXThisTypeOverride.isNull();
100+
101+
if (auto *MD = dyn_cast<CXXMethodDecl>(DC)) {
102+
if (MD->isImplicitObjectMemberFunction()) {
103+
isStaticOrExplicitContext = false;
104+
// A dependent class scope function template explicit specialization
105+
// that is neither declared 'static' nor with an explicit object
106+
// parameter could instantiate to a static or non-static member function.
107+
couldInstantiateToStatic = MD->getDependentSpecializationInfo();
108+
}
109+
}
98110

99111
if (R.isUnresolvableResult())
100112
return isStaticOrExplicitContext ? IMA_Unresolved_StaticOrExplicitContext
@@ -123,6 +135,9 @@ static IMAKind ClassifyImplicitMemberAccess(Sema &SemaRef,
123135
if (Classes.empty())
124136
return IMA_Static;
125137

138+
if (couldInstantiateToStatic)
139+
return IMA_Dependent;
140+
126141
// C++11 [expr.prim.general]p12:
127142
// An id-expression that denotes a non-static data member or non-static
128143
// member function of a class can only be used:
@@ -268,27 +283,30 @@ ExprResult Sema::BuildPossibleImplicitMemberExpr(
268283
const CXXScopeSpec &SS, SourceLocation TemplateKWLoc, LookupResult &R,
269284
const TemplateArgumentListInfo *TemplateArgs, const Scope *S,
270285
UnresolvedLookupExpr *AsULE) {
271-
switch (ClassifyImplicitMemberAccess(*this, R)) {
286+
switch (IMAKind Classification = ClassifyImplicitMemberAccess(*this, R)) {
272287
case IMA_Instance:
273-
return BuildImplicitMemberExpr(SS, TemplateKWLoc, R, TemplateArgs, true, S);
274-
275288
case IMA_Mixed:
276289
case IMA_Mixed_Unrelated:
277290
case IMA_Unresolved:
278-
return BuildImplicitMemberExpr(SS, TemplateKWLoc, R, TemplateArgs, false,
279-
S);
280-
291+
return BuildImplicitMemberExpr(
292+
SS, TemplateKWLoc, R, TemplateArgs,
293+
/*IsKnownInstance=*/Classification == IMA_Instance, S);
281294
case IMA_Field_Uneval_Context:
282295
Diag(R.getNameLoc(), diag::warn_cxx98_compat_non_static_member_use)
283296
<< R.getLookupNameInfo().getName();
284297
[[fallthrough]];
285298
case IMA_Static:
286299
case IMA_Abstract:
300+
case IMA_Dependent:
287301
case IMA_Mixed_StaticOrExplicitContext:
288302
case IMA_Unresolved_StaticOrExplicitContext:
289303
if (TemplateArgs || TemplateKWLoc.isValid())
290-
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, false, TemplateArgs);
291-
return AsULE ? AsULE : BuildDeclarationNameExpr(SS, R, false);
304+
return BuildTemplateIdExpr(SS, TemplateKWLoc, R, /*RequiresADL=*/false,
305+
TemplateArgs);
306+
return AsULE ? AsULE
307+
: BuildDeclarationNameExpr(
308+
SS, R, /*NeedsADL=*/false, /*AcceptInvalidDecl=*/false,
309+
/*NeedUnresolved=*/Classification == IMA_Dependent);
292310

293311
case IMA_Error_StaticOrExplicitContext:
294312
case IMA_Error_Unrelated:

clang/lib/Sema/SemaTemplateInstantiateDecl.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5093,6 +5093,14 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
50935093
EnterExpressionEvaluationContext EvalContext(
50945094
*this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
50955095

5096+
Qualifiers ThisTypeQuals;
5097+
CXXRecordDecl *ThisContext = nullptr;
5098+
if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Function)) {
5099+
ThisContext = Method->getParent();
5100+
ThisTypeQuals = Method->getMethodQualifiers();
5101+
}
5102+
CXXThisScopeRAII ThisScope(*this, ThisContext, ThisTypeQuals);
5103+
50965104
// Introduce a new scope where local variable instantiations will be
50975105
// recorded, unless we're actually a member function within a local
50985106
// class, in which case we need to merge our results with the parent

clang/lib/Sema/TreeTransform.h

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,9 @@ class TreeTransform {
794794
ParenExpr *PE, DependentScopeDeclRefExpr *DRE, bool IsAddressOfOperand,
795795
TypeSourceInfo **RecoveryTSI);
796796

797+
ExprResult TransformUnresolvedLookupExpr(UnresolvedLookupExpr *E,
798+
bool IsAddressOfOperand);
799+
797800
StmtResult TransformOMPExecutableDirective(OMPExecutableDirective *S);
798801

799802
// FIXME: We use LLVM_ATTRIBUTE_NOINLINE because inlining causes a ridiculous
@@ -3307,12 +3310,13 @@ class TreeTransform {
33073310

33083311
/// Build a new C++ "this" expression.
33093312
///
3310-
/// By default, builds a new "this" expression without performing any
3311-
/// semantic analysis. Subclasses may override this routine to provide
3312-
/// different behavior.
3313+
/// By default, performs semantic analysis to build a new "this" expression.
3314+
/// Subclasses may override this routine to provide different behavior.
33133315
ExprResult RebuildCXXThisExpr(SourceLocation ThisLoc,
33143316
QualType ThisType,
33153317
bool isImplicit) {
3318+
if (getSema().CheckCXXThisType(ThisLoc, ThisType))
3319+
return ExprError();
33163320
return getSema().BuildCXXThisExpr(ThisLoc, ThisType, isImplicit);
33173321
}
33183322

@@ -11313,7 +11317,11 @@ template<typename Derived>
1131311317
ExprResult
1131411318
TreeTransform<Derived>::TransformAddressOfOperand(Expr *E) {
1131511319
if (DependentScopeDeclRefExpr *DRE = dyn_cast<DependentScopeDeclRefExpr>(E))
11316-
return getDerived().TransformDependentScopeDeclRefExpr(DRE, true, nullptr);
11320+
return getDerived().TransformDependentScopeDeclRefExpr(
11321+
DRE, /*IsAddressOfOperand=*/true, nullptr);
11322+
else if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(E))
11323+
return getDerived().TransformUnresolvedLookupExpr(
11324+
ULE, /*IsAddressOfOperand=*/true);
1131711325
else
1131811326
return getDerived().TransformExpr(E);
1131911327
}
@@ -13019,10 +13027,16 @@ bool TreeTransform<Derived>::TransformOverloadExprDecls(OverloadExpr *Old,
1301913027
return false;
1302013028
}
1302113029

13022-
template<typename Derived>
13030+
template <typename Derived>
13031+
ExprResult TreeTransform<Derived>::TransformUnresolvedLookupExpr(
13032+
UnresolvedLookupExpr *Old) {
13033+
return TransformUnresolvedLookupExpr(Old, /*IsAddressOfOperand=*/false);
13034+
}
13035+
13036+
template <typename Derived>
1302313037
ExprResult
13024-
TreeTransform<Derived>::TransformUnresolvedLookupExpr(
13025-
UnresolvedLookupExpr *Old) {
13038+
TreeTransform<Derived>::TransformUnresolvedLookupExpr(UnresolvedLookupExpr *Old,
13039+
bool IsAddressOfOperand) {
1302613040
LookupResult R(SemaRef, Old->getName(), Old->getNameLoc(),
1302713041
Sema::LookupOrdinaryName);
1302813042

@@ -13056,6 +13070,7 @@ TreeTransform<Derived>::TransformUnresolvedLookupExpr(
1305613070

1305713071
SourceLocation TemplateKWLoc = Old->getTemplateKeywordLoc();
1305813072

13073+
#if 0
1305913074
// If we have neither explicit template arguments, nor the template keyword,
1306013075
// it's a normal declaration name or member reference.
1306113076
if (!Old->hasExplicitTemplateArgs() && !TemplateKWLoc.isValid()) {
@@ -13071,6 +13086,20 @@ TreeTransform<Derived>::TransformUnresolvedLookupExpr(
1307113086

1307213087
return getDerived().RebuildDeclarationNameExpr(SS, R, Old->requiresADL());
1307313088
}
13089+
#endif
13090+
13091+
bool PotentiallyImplicitAccess =
13092+
R.isClassLookup() &&
13093+
(!IsAddressOfOperand ||
13094+
(R.isSingleResult() &&
13095+
R.getAsSingle<NamedDecl>()->isCXXInstanceMember()));
13096+
13097+
// If we have neither explicit template arguments, nor the template keyword,
13098+
// it's a normal declaration name or member reference.
13099+
if (!PotentiallyImplicitAccess && !Old->hasExplicitTemplateArgs() &&
13100+
!TemplateKWLoc.isValid()) {
13101+
return getDerived().RebuildDeclarationNameExpr(SS, R, Old->requiresADL());
13102+
}
1307413103

1307513104
// If we have template arguments, rebuild them, then rebuild the
1307613105
// templateid expression.
@@ -13083,6 +13112,16 @@ TreeTransform<Derived>::TransformUnresolvedLookupExpr(
1308313112
return ExprError();
1308413113
}
1308513114

13115+
// In a C++11 unevaluated context, an UnresolvedLookupExpr might refer to an
13116+
// instance member. In other contexts, BuildPossibleImplicitMemberExpr will
13117+
// give a good diagnostic.
13118+
if (PotentiallyImplicitAccess) {
13119+
return SemaRef.BuildPossibleImplicitMemberExpr(
13120+
SS, TemplateKWLoc, R,
13121+
Old->hasExplicitTemplateArgs() ? &TransArgs : nullptr,
13122+
/*Scope=*/nullptr);
13123+
}
13124+
1308613125
return getDerived().RebuildTemplateIdExpr(SS, TemplateKWLoc, R,
1308713126
Old->requiresADL(), &TransArgs);
1308813127
}

clang/test/SemaTemplate/ms-function-specialization-class-scope.cpp

Lines changed: 56 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
// RUN: %clang_cc1 -fms-extensions -fsyntax-only -verify %s
2-
// RUN: %clang_cc1 -fms-extensions -fdelayed-template-parsing -fsyntax-only -verify %s
1+
// RUN: %clang_cc1 -fms-extensions -fsyntax-only -Wno-unused-value -verify %s
2+
// RUN: %clang_cc1 -fms-extensions -fdelayed-template-parsing -fsyntax-only -Wno-unused-value -verify %s
33

4-
// expected-no-diagnostics
54
class A {
65
public:
76
template<class U> A(U p) {}
@@ -76,3 +75,57 @@ struct S {
7675
int f<0>(int);
7776
};
7877
}
78+
79+
namespace UsesThis {
80+
template<typename T>
81+
struct A {
82+
int x;
83+
84+
template<typename U = void>
85+
static void f();
86+
87+
template<>
88+
void f<int>() {
89+
this->x; // expected-error {{invalid use of 'this' outside of a non-static member function}}
90+
x; // expected-error {{invalid use of member 'x' in static member function}}
91+
A::x; // expected-error {{invalid use of member 'x' in static member function}}
92+
+x; // expected-error {{invalid use of member 'x' in static member function}}
93+
+A::x; // expected-error {{invalid use of member 'x' in static member function}}
94+
f();
95+
f<void>();
96+
g(); // expected-error {{call to non-static member function without an object argument}}
97+
g<void>(); // expected-error {{call to non-static member function without an object argument}}
98+
i(); // expected-error {{call to non-static member function without an object argument}}
99+
j();
100+
}
101+
102+
template<typename U = void>
103+
void g();
104+
105+
template<>
106+
void g<int>() {
107+
this->x;
108+
x;
109+
A::x;
110+
+x;
111+
+A::x;
112+
f();
113+
f<void>();
114+
g();
115+
g<void>();
116+
i();
117+
j();
118+
}
119+
120+
template<typename U>
121+
static auto h() -> A*;
122+
123+
template<>
124+
auto h<int>() -> decltype(this); // expected-error {{'this' cannot be used in a static member function declaration}}
125+
126+
void i();
127+
static void j();
128+
};
129+
130+
template struct A<int>; // expected-note 2{{in instantiation of}}
131+
}

0 commit comments

Comments
 (0)