Skip to content

Commit

Permalink
Diagnose implicit member accesses when qualified by a splice.
Browse files Browse the repository at this point in the history
  • Loading branch information
katzdm committed May 25, 2024
1 parent 3d27e82 commit 0994a5a
Show file tree
Hide file tree
Showing 3 changed files with 104 additions and 9 deletions.
14 changes: 14 additions & 0 deletions clang/lib/Sema/SemaExprMember.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2056,6 +2056,20 @@ Sema::BuildImplicitMemberExpr(const CXXScopeSpec &SS,

SourceLocation loc = R.getNameLoc();

if (auto *NNS = SS.getScopeRep()) {
if (auto *Prefix = NNS->getPrefix())
NNS = Prefix;

if (NNS->getAsSpliceExpr()) {
Diag(SS.getBeginLoc(),
diag::err_dependent_splice_implicit_member_reference)
<< SourceRange(SS.getBeginLoc(), R.getNameLoc());
Diag(SS.getBeginLoc(),
diag::note_dependent_splice_explicit_this_may_fix);
return ExprError();
}
}

// If this is known to be an instance access, go ahead and build an
// implicit 'this' expression now.
QualType ThisTy = getCurrentThisType();
Expand Down
36 changes: 28 additions & 8 deletions clang/lib/Sema/TreeTransform.h
Original file line number Diff line number Diff line change
Expand Up @@ -14436,26 +14436,46 @@ ExprResult TreeTransform<Derived>::TransformDependentScopeDeclRefExpr(
if (!NameInfo.getName())
return ExprError();

bool ScopeHadLeadingSplice = false;
if (auto *NNS = E->getQualifier()) {
if (auto *Prefix = NNS->getPrefix())
NNS = Prefix;

ScopeHadLeadingSplice = (NNS->getAsSpliceExpr() != nullptr);
}

ExprResult Result;
if (!E->hasExplicitTemplateArgs()) {
if (!getDerived().AlwaysRebuild() && QualifierLoc == E->getQualifierLoc() &&
// Note: it is sufficient to compare the Name component of NameInfo:
// if name has not changed, DNLoc has not changed either.
NameInfo.getName() == E->getDeclName())
return E;

return getDerived().RebuildDependentScopeDeclRefExpr(
Result = getDerived().RebuildDependentScopeDeclRefExpr(
QualifierLoc, TemplateKWLoc, NameInfo, /*TemplateArgs=*/nullptr,
IsAddressOfOperand, RecoveryTSI);
} else {
TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc());
if (getDerived().TransformTemplateArguments(
E->getTemplateArgs(), E->getNumTemplateArgs(), TransArgs))
return ExprError();

Result = getDerived().RebuildDependentScopeDeclRefExpr(
QualifierLoc, TemplateKWLoc, NameInfo, &TransArgs, IsAddressOfOperand,
RecoveryTSI);
}

TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc());
if (getDerived().TransformTemplateArguments(
E->getTemplateArgs(), E->getNumTemplateArgs(), TransArgs))
if (auto *MRE = dyn_cast_or_null<MemberExpr>(Result.get());
ScopeHadLeadingSplice && MRE && MRE->isImplicitAccess()) {
SemaRef.Diag(E->getExprLoc(),
diag::err_dependent_splice_implicit_member_reference)
<< E->getSourceRange();
SemaRef.Diag(E->getExprLoc(),
diag::note_dependent_splice_explicit_this_may_fix);
return ExprError();

return getDerived().RebuildDependentScopeDeclRefExpr(
QualifierLoc, TemplateKWLoc, NameInfo, &TransArgs, IsAddressOfOperand,
RecoveryTSI);
}
return Result;
}

template<typename Derived>
Expand Down
63 changes: 62 additions & 1 deletion clang/test/Reflection/splice-exprs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
//
//===----------------------------------------------------------------------===//
//
// RUN: %clang_cc1 %s -std=c++23 -freflection
// RUN: %clang_cc1 %s -std=c++23 -freflection -verify

using info = decltype(^int);

Expand Down Expand Up @@ -204,6 +204,67 @@ int dK = d.[:^S::k:];

} // namespace with_member_access

// ===========================
// with_implicit_member_access
// ===========================

namespace with_implicit_member_access {

// Non-dependent case
struct S {
static constexpr int l = 3;

int k;

void fn2() { }

void fn() {
static_assert([:^l:] == 3);
static_assert([:^S:]::l == 3);
(void) this->[:^k:];
(void) this->[:^S:]::k;
this->[:^fn2:]();
this->[:^S:]::fn2();

(void) [:^k:]; // expected-error {{cannot implicitly reference}} \
// expected-note {{explicit 'this' pointer}}
(void) [:^S:]::k; // expected-error {{cannot implicitly reference}} \
// expected-note {{explicit 'this' pointer}}
[:^fn:](); // expected-error {{cannot implicitly reference}} \
// expected-note {{explicit 'this' pointer}}
[:^S:]::fn2(); // expected-error {{cannot implicitly reference}} \
// expected-note {{explicit 'this' pointer}}
}
};

// Dependent case
struct D {
static constexpr int l = 3;

int k;

void fn2() { }

template <typename T>
void fn() {
static_assert([:^T:]::l == 3);
(void) this->[:^T:]::l;
(void) this->[:^T:]::fn2();

(void) [:^T:]::k; // expected-error {{cannot implicitly reference}} \
// expected-note {{explicit 'this' pointer}}
[:^T:]::fn2(); // expected-error {{cannot implicitly reference}} \
// expected-note {{explicit 'this' pointer}}
}
};

void runner() {
D f = {4};
f.fn<D>(); // expected-note {{in instantiation of function template}}
}

} // namespace with_implicit_member_access

// ======================
// with_overridden_memfns
// ======================
Expand Down

0 comments on commit 0994a5a

Please sign in to comment.