Skip to content

Commit 633d867

Browse files
committed
Restrict reflection operators within lambdas.
1 parent f70f4ab commit 633d867

File tree

4 files changed

+96
-20
lines changed

4 files changed

+96
-20
lines changed

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3106,10 +3106,9 @@ def err_reflect_nttp : Error<
31063106
"cannot take the reflection of a non-type template parameter">;
31073107
def err_reflect_init_capture : Error<
31083108
"cannot take the reflection of a local entity declared by init-capture">;
3109-
def err_template_arg_bad_reflection_kind : Error<
3110-
"reflection of this kind cannot appear as a template argument">;
3111-
def err_reflect_dependent_splice : Error<
3112-
"cannot take the reflection of a dependent splice expression">;
3109+
def err_reflect_intervening_lambda : Error<
3110+
"cannot take the reflection of a local entity for which there is an "
3111+
"intervening lambda expression">;
31133112

31143113
def err_splice_operand_not_reflection : Error<
31153114
"splice operand must be a reflection">;
@@ -3142,6 +3141,9 @@ def err_dependent_splice_implicit_member_reference : Error<
31423141
"cannot implicitly reference a class member through a splice">;
31433142
def note_dependent_splice_explicit_this_may_fix : Note<
31443143
"an explicit 'this' pointer may fix the problem">;
3144+
def err_splice_intervening_lambda : Error<
3145+
"cannot splice local entity %0 for which there is an intervening lambda "
3146+
"expression">;
31453147

31463148
def err_metafunction_empty_args : Error<
31473149
"__metafunction requires arguments">;

clang/lib/Sema/SemaReflect.cpp

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ Expr *CreateRefToDecl(Sema &S, ValueDecl *D, SourceLocation ExprLoc) {
8989
}
9090
}
9191

92-
static Decl *findInjectionCone(Decl *ContainingDecl) {
92+
Decl *findInjectionCone(Decl *ContainingDecl) {
9393
for (Decl *Ctx = ContainingDecl; Ctx;
9494
Ctx = cast<Decl>(Ctx->getDeclContext())) {
9595
if (isa<RecordDecl, FunctionDecl, TranslationUnitDecl>(Ctx))
@@ -98,6 +98,56 @@ static Decl *findInjectionCone(Decl *ContainingDecl) {
9898
llvm_unreachable("should have terminated at a TranslationUnitDecl");
9999
}
100100

101+
bool CheckReflectVar(Sema &S, VarDecl *VD, SourceRange Range) {
102+
// Reflections of 'init-capture's are always ill-formed.
103+
if (VD->isInitCapture()) {
104+
S.Diag(Range.getBegin(), diag::err_reflect_init_capture) << Range;
105+
return true;
106+
}
107+
108+
// All other cases that aren't local entities are fine.
109+
if (!VD->isLocalVarDeclOrParm() || VD->isStaticLocal())
110+
return false;
111+
112+
// Check for an intervening lambda scope.
113+
for (DeclContext *DC = S.CurContext; DC != VD->getDeclContext();
114+
DC = DC->getParent()) {
115+
assert(DC && "Var context not a parent of the current context");
116+
if (auto *RD = dyn_cast<CXXRecordDecl>(DC); RD && RD->isLambda()) {
117+
S.Diag(Range.getBegin(), diag::err_reflect_intervening_lambda) << Range;
118+
return true;
119+
}
120+
}
121+
return false;
122+
}
123+
124+
bool CheckSpliceVar(Sema &S, VarDecl *VD, SourceRange Range) {
125+
// All non-local entities are fine.
126+
if (!VD->isLocalVarDeclOrParm() || VD->isStaticLocal())
127+
return false;
128+
129+
// Unevaluated contexts are fine.
130+
//
131+
// We should also ignore any enclosing 'typeid' expressions, but clang (as far
132+
// as I can tell) doesn't implement that for lambda captures either, so we
133+
// likewise ignore that here.
134+
if (!S.currentEvaluationContext().isPotentiallyEvaluated())
135+
return false;
136+
137+
// Check for an intervening lambda scope.
138+
for (DeclContext *DC = S.CurContext; DC != VD->getDeclContext();
139+
DC = DC->getParent()) {
140+
assert(DC && "Var context not a parent of the current context");
141+
if (auto *RD = dyn_cast<CXXRecordDecl>(DC); RD && RD->isLambda()) {
142+
S.Diag(Range.getBegin(), diag::err_splice_intervening_lambda)
143+
<< VD << Range;
144+
S.Diag(VD->getLocation(), diag::note_entity_declared_at) << VD;
145+
return true;
146+
}
147+
}
148+
return false;
149+
}
150+
101151
class MetaActionsImpl : public MetaActions {
102152
Sema &S;
103153

@@ -828,12 +878,8 @@ ExprResult Sema::ActOnCXXReflectExpr(SourceLocation OpLoc,
828878
return BuildCXXReflectExpr(OpLoc, NameInfo.getBeginLoc(), ND);
829879

830880
if (auto *VD = dyn_cast<VarDecl>(ND);
831-
VD && (VD->isInitCapture())) {
832-
Diag(Id.StartLocation, diag::err_reflect_init_capture)
833-
<< Id.getSourceRange();
881+
VD && CheckReflectVar(*this, VD, Id.getSourceRange()))
834882
return ExprError();
835-
}
836-
837883

838884
// Why do we have to build an expression here? Just stash in an APValue?
839885
if (isa<VarDecl, BindingDecl, FunctionDecl, FieldDecl, EnumConstantDecl,
@@ -1527,6 +1573,10 @@ ExprResult Sema::BuildReflectionSpliceExpr(SourceLocation TemplateKWLoc,
15271573
return ExprError();
15281574
}
15291575

1576+
if (auto *VD = dyn_cast<VarDecl>(TheDecl);
1577+
VD && CheckSpliceVar(*this, VD, Splice->getSourceRange()))
1578+
return ExprError();
1579+
15301580
// Create a new DeclRefExpr, since the operand of the reflect expression
15311581
// was parsed in an unevaluated context (but a splice expression is not
15321582
// necessarily, and frequently not, in such a context).

clang/test/Reflection/lift-operator.cpp

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//
99
//===----------------------------------------------------------------------===//
1010
//
11-
// RUN: %clang_cc1 %s -std=c++23 -freflection
11+
// RUN: %clang_cc1 %s -std=c++23 -freflection -verify
1212

1313
// Reflecting Types
1414
using info = decltype(^^void);
@@ -166,18 +166,26 @@ consteval int fn(decltype(^^::) x = ^^x) { return 0; }
166166
constexpr int x = fn();
167167
} // namspace self_reference
168168

169-
// ==============
170-
// lambda_capture
171-
// ==============
169+
// =================
170+
// enclosing_lambdas
171+
// =================
172172

173-
namespace lambda_capture {
173+
namespace enclosing_lambdas {
174+
static int s1;
174175
void fn() {
175-
int x;
176-
[=]<auto r> {
177-
static_assert(^^x == r);
178-
}.operator()<^^x>();
176+
int l1;
177+
static int s2;
178+
constexpr auto rl1 = ^^l1;
179+
(void) [] -> decltype(^^s1, ^^l1, s2) {
180+
// expected-error@-1 {{intervening lambda expression}}
181+
int l2;
182+
183+
constexpr auto rl1_2 = ^^l1;
184+
// expected-error@-1 {{intervening lambda expression}}
185+
constexpr auto rl2 = ^^l2;
186+
};
179187
}
180-
} // namespace lambda_capture
188+
} // namespace enclosing_lambdas
181189

182190
// =======================================
183191
// bb_clang_p2996_issue_35_regression_test

clang/test/Reflection/splice-expr-errors.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,19 @@ void fn([:^^int:]);
6868
// expected-error@-1 {{not usable in a splice expression}}
6969

7070
} // namespace parameter_declaration_ambiguity
71+
72+
// =================
73+
// enclosing_lambdas
74+
// =================
75+
76+
namespace enclosing_lambdas {
77+
void fn() {
78+
int x = 1; // expected-note {{'x' declared here}}
79+
constexpr auto r = ^^x;
80+
81+
(void) [] -> decltype([:r:]) {
82+
return [:r:];
83+
// expected-error@-1 {{'x' for which there is an intervening lambda}}
84+
};
85+
}
86+
} // namespace enclosing_lambdas

0 commit comments

Comments
 (0)