Skip to content

Commit 5c1db38

Browse files
authored
[Clang] Functions called in discarded statements should not be instantiated (#140576)
Functions referenced in discarded statements could be treated as odr-used because we did not properly set the correct evaluation context in some places. Fixes #140449
1 parent d561d59 commit 5c1db38

File tree

8 files changed

+127
-73
lines changed

8 files changed

+127
-73
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,7 @@ Bug Fixes to C++ Support
745745
- Fix an incorrect deduction when calling an explicit object member function template through an overload set address.
746746
- Fixed bug in constant evaluation that would allow using the value of a
747747
reference in its own initializer in C++23 mode (#GH131330).
748+
- Clang could incorrectly instantiate functions in discarded contexts (#GH140449)
748749

749750
Bug Fixes to AST Handling
750751
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/EnterExpressionEvaluationContext.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,22 @@ class EnterExpressionEvaluationContext {
6464
}
6565
};
6666

67+
/// RAII object that enters a new function expression evaluation context.
68+
class EnterExpressionEvaluationContextForFunction {
69+
Sema &Actions;
70+
71+
public:
72+
EnterExpressionEvaluationContextForFunction(
73+
Sema &Actions, Sema::ExpressionEvaluationContext NewContext,
74+
FunctionDecl *FD = nullptr)
75+
: Actions(Actions) {
76+
Actions.PushExpressionEvaluationContextForFunction(NewContext, FD);
77+
}
78+
~EnterExpressionEvaluationContextForFunction() {
79+
Actions.PopExpressionEvaluationContext();
80+
}
81+
};
82+
6783
} // namespace clang
6884

6985
#endif

clang/include/clang/Sema/Sema.h

Lines changed: 11 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6825,8 +6825,9 @@ class Sema final : public SemaBase {
68256825

68266826
bool isDiscardedStatementContext() const {
68276827
return Context == ExpressionEvaluationContext::DiscardedStatement ||
6828-
(Context ==
6829-
ExpressionEvaluationContext::ImmediateFunctionContext &&
6828+
((Context ==
6829+
ExpressionEvaluationContext::ImmediateFunctionContext ||
6830+
isPotentiallyEvaluated()) &&
68306831
InDiscardedStatement);
68316832
}
68326833
};
@@ -6915,6 +6916,10 @@ class Sema final : public SemaBase {
69156916
ExpressionEvaluationContext NewContext, Decl *LambdaContextDecl = nullptr,
69166917
ExpressionEvaluationContextRecord::ExpressionKind Type =
69176918
ExpressionEvaluationContextRecord::EK_Other);
6919+
6920+
void PushExpressionEvaluationContextForFunction(
6921+
ExpressionEvaluationContext NewContext, FunctionDecl *FD);
6922+
69186923
enum ReuseLambdaContextDecl_t { ReuseLambdaContextDecl };
69196924
void PushExpressionEvaluationContext(
69206925
ExpressionEvaluationContext NewContext, ReuseLambdaContextDecl_t,
@@ -13371,23 +13376,11 @@ class Sema final : public SemaBase {
1337113376
: S(S), SavedContext(S, DC) {
1337213377
auto *FD = dyn_cast<FunctionDecl>(DC);
1337313378
S.PushFunctionScope();
13374-
S.PushExpressionEvaluationContext(
13375-
(FD && FD->isImmediateFunction())
13376-
? ExpressionEvaluationContext::ImmediateFunctionContext
13377-
: ExpressionEvaluationContext::PotentiallyEvaluated);
13378-
if (FD) {
13379-
auto &Current = S.currentEvaluationContext();
13380-
const auto &Parent = S.parentEvaluationContext();
13381-
13379+
S.PushExpressionEvaluationContextForFunction(
13380+
ExpressionEvaluationContext::PotentiallyEvaluated, FD);
13381+
if (FD)
1338213382
FD->setWillHaveBody(true);
13383-
Current.InImmediateFunctionContext =
13384-
FD->isImmediateFunction() ||
13385-
(isLambdaMethod(FD) && (Parent.isConstantEvaluated() ||
13386-
Parent.isImmediateFunctionContext()));
13387-
13388-
Current.InImmediateEscalatingFunctionContext =
13389-
S.getLangOpts().CPlusPlus20 && FD->isImmediateEscalating();
13390-
} else
13383+
else
1339113384
assert(isa<ObjCMethodDecl>(DC));
1339213385
}
1339313386

clang/lib/Sema/SemaCoroutine.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -697,8 +697,10 @@ static void checkReturnStmtInCoroutine(Sema &S, FunctionScopeInfo *FSI) {
697697
bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc,
698698
StringRef Keyword) {
699699
// Ignore previous expr evaluation contexts.
700-
EnterExpressionEvaluationContext PotentiallyEvaluated(
701-
*this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
700+
EnterExpressionEvaluationContextForFunction PotentiallyEvaluated(
701+
*this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
702+
dyn_cast_or_null<FunctionDecl>(CurContext));
703+
702704
if (!checkCoroutineContext(*this, KWLoc, Keyword))
703705
return false;
704706
auto *ScopeInfo = getCurFunction();

clang/lib/Sema/SemaDecl.cpp

Lines changed: 2 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15870,24 +15870,8 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
1587015870
// Do not push if it is a lambda because one is already pushed when building
1587115871
// the lambda in ActOnStartOfLambdaDefinition().
1587215872
if (!isLambdaCallOperator(FD))
15873-
// [expr.const]/p14.1
15874-
// An expression or conversion is in an immediate function context if it is
15875-
// potentially evaluated and either: its innermost enclosing non-block scope
15876-
// is a function parameter scope of an immediate function.
15877-
PushExpressionEvaluationContext(
15878-
FD->isConsteval() ? ExpressionEvaluationContext::ImmediateFunctionContext
15879-
: ExprEvalContexts.back().Context);
15880-
15881-
// Each ExpressionEvaluationContextRecord also keeps track of whether the
15882-
// context is nested in an immediate function context, so smaller contexts
15883-
// that appear inside immediate functions (like variable initializers) are
15884-
// considered to be inside an immediate function context even though by
15885-
// themselves they are not immediate function contexts. But when a new
15886-
// function is entered, we need to reset this tracking, since the entered
15887-
// function might be not an immediate function.
15888-
ExprEvalContexts.back().InImmediateFunctionContext = FD->isConsteval();
15889-
ExprEvalContexts.back().InImmediateEscalatingFunctionContext =
15890-
getLangOpts().CPlusPlus20 && FD->isImmediateEscalating();
15873+
PushExpressionEvaluationContextForFunction(ExprEvalContexts.back().Context,
15874+
FD);
1589115875

1589215876
// Check for defining attributes before the check for redefinition.
1589315877
if (const auto *Attr = FD->getAttr<AliasAttr>()) {

clang/lib/Sema/SemaExpr.cpp

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -17748,6 +17748,47 @@ Sema::PushExpressionEvaluationContext(
1774817748
PushExpressionEvaluationContext(NewContext, ClosureContextDecl, ExprContext);
1774917749
}
1775017750

17751+
void Sema::PushExpressionEvaluationContextForFunction(
17752+
ExpressionEvaluationContext NewContext, FunctionDecl *FD) {
17753+
// [expr.const]/p14.1
17754+
// An expression or conversion is in an immediate function context if it is
17755+
// potentially evaluated and either: its innermost enclosing non-block scope
17756+
// is a function parameter scope of an immediate function.
17757+
PushExpressionEvaluationContext(
17758+
FD && FD->isConsteval()
17759+
? ExpressionEvaluationContext::ImmediateFunctionContext
17760+
: NewContext);
17761+
const Sema::ExpressionEvaluationContextRecord &Parent =
17762+
parentEvaluationContext();
17763+
Sema::ExpressionEvaluationContextRecord &Current = currentEvaluationContext();
17764+
17765+
Current.InDiscardedStatement = false;
17766+
17767+
if (FD) {
17768+
17769+
// Each ExpressionEvaluationContextRecord also keeps track of whether the
17770+
// context is nested in an immediate function context, so smaller contexts
17771+
// that appear inside immediate functions (like variable initializers) are
17772+
// considered to be inside an immediate function context even though by
17773+
// themselves they are not immediate function contexts. But when a new
17774+
// function is entered, we need to reset this tracking, since the entered
17775+
// function might be not an immediate function.
17776+
17777+
Current.InImmediateEscalatingFunctionContext =
17778+
getLangOpts().CPlusPlus20 && FD->isImmediateEscalating();
17779+
17780+
if (isLambdaMethod(FD)) {
17781+
Current.InDiscardedStatement = Parent.isDiscardedStatementContext();
17782+
Current.InImmediateFunctionContext =
17783+
FD->isConsteval() ||
17784+
(isLambdaMethod(FD) && (Parent.isConstantEvaluated() ||
17785+
Parent.isImmediateFunctionContext()));
17786+
} else {
17787+
Current.InImmediateFunctionContext = FD->isConsteval();
17788+
}
17789+
}
17790+
}
17791+
1775117792
namespace {
1775217793

1775317794
const DeclRefExpr *CheckPossibleDeref(Sema &S, const Expr *PossibleDeref) {
@@ -18360,35 +18401,23 @@ enum class OdrUseContext {
1836018401
/// Are we within a context in which references to resolved functions or to
1836118402
/// variables result in odr-use?
1836218403
static OdrUseContext isOdrUseContext(Sema &SemaRef) {
18363-
OdrUseContext Result;
18364-
18365-
switch (SemaRef.ExprEvalContexts.back().Context) {
18366-
case Sema::ExpressionEvaluationContext::Unevaluated:
18367-
case Sema::ExpressionEvaluationContext::UnevaluatedList:
18368-
case Sema::ExpressionEvaluationContext::UnevaluatedAbstract:
18369-
return OdrUseContext::None;
18370-
18371-
case Sema::ExpressionEvaluationContext::ConstantEvaluated:
18372-
case Sema::ExpressionEvaluationContext::ImmediateFunctionContext:
18373-
case Sema::ExpressionEvaluationContext::PotentiallyEvaluated:
18374-
Result = OdrUseContext::Used;
18375-
break;
18404+
const Sema::ExpressionEvaluationContextRecord &Context =
18405+
SemaRef.currentEvaluationContext();
1837618406

18377-
case Sema::ExpressionEvaluationContext::DiscardedStatement:
18378-
Result = OdrUseContext::FormallyOdrUsed;
18379-
break;
18380-
18381-
case Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed:
18382-
// A default argument formally results in odr-use, but doesn't actually
18383-
// result in a use in any real sense until it itself is used.
18384-
Result = OdrUseContext::FormallyOdrUsed;
18385-
break;
18386-
}
18407+
if (Context.isUnevaluated())
18408+
return OdrUseContext::None;
1838718409

1838818410
if (SemaRef.CurContext->isDependentContext())
1838918411
return OdrUseContext::Dependent;
1839018412

18391-
return Result;
18413+
if (Context.isDiscardedStatementContext())
18414+
return OdrUseContext::FormallyOdrUsed;
18415+
18416+
else if (Context.Context ==
18417+
Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed)
18418+
return OdrUseContext::FormallyOdrUsed;
18419+
18420+
return OdrUseContext::Used;
1839218421
}
1839318422

1839418423
static bool isImplicitlyDefinableConstexprFunction(FunctionDecl *Func) {
@@ -20355,9 +20384,11 @@ MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, Decl *D, Expr *E,
2035520384
}
2035620385

2035720386
void Sema::MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base) {
20358-
// TODO: update this with DR# once a defect report is filed.
20359-
// C++11 defect. The address of a pure member should not be an ODR use, even
20360-
// if it's a qualified reference.
20387+
// [basic.def.odr] (CWG 1614)
20388+
// A function is named by an expression or conversion [...]
20389+
// unless it is a pure virtual function and either the expression is not an
20390+
// id-expression naming the function with an explicitly qualified name or
20391+
// the expression forms a pointer to member
2036120392
bool OdrUse = true;
2036220393
if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(E->getDecl()))
2036320394
if (Method->isVirtual() &&

clang/lib/Sema/SemaLambda.cpp

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1574,14 +1574,8 @@ void Sema::ActOnStartOfLambdaDefinition(LambdaIntroducer &Intro,
15741574

15751575
// Enter a new evaluation context to insulate the lambda from any
15761576
// cleanups from the enclosing full-expression.
1577-
PushExpressionEvaluationContext(
1578-
LSI->CallOperator->isConsteval()
1579-
? ExpressionEvaluationContext::ImmediateFunctionContext
1580-
: ExpressionEvaluationContext::PotentiallyEvaluated);
1581-
ExprEvalContexts.back().InImmediateFunctionContext =
1582-
LSI->CallOperator->isConsteval();
1583-
ExprEvalContexts.back().InImmediateEscalatingFunctionContext =
1584-
getLangOpts().CPlusPlus20 && LSI->CallOperator->isImmediateEscalating();
1577+
PushExpressionEvaluationContextForFunction(
1578+
ExpressionEvaluationContext::PotentiallyEvaluated, LSI->CallOperator);
15851579
}
15861580

15871581
void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope,

clang/test/CXX/stmt.stmt/stmt.select/stmt.if/p2.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,37 @@ void f() {
177177
}
178178
} // namespace deduced_return_type_in_discareded_statement
179179

180+
namespace GH140449 {
181+
182+
template <typename T>
183+
int f() {
184+
T *ptr;
185+
return 0;
186+
}
187+
188+
template <typename T>
189+
constexpr int g() {
190+
T *ptr; // expected-error{{'ptr' declared as a pointer to a reference of type 'int &'}}
191+
return 0;
192+
}
193+
194+
template <typename T>
195+
auto h() {
196+
T *ptr; // expected-error{{'ptr' declared as a pointer to a reference of type 'int &'}}
197+
return 0;
198+
}
199+
200+
void test() {
201+
if constexpr (false) {
202+
int x = f<int &>();
203+
constexpr int y = g<int &>();
204+
// expected-error@-1 {{constexpr variable 'y' must be initialized by a constant expression}} \
205+
// expected-note@-1{{in instantiation of function template specialization}}
206+
int z = h<int &>();
207+
// expected-note@-1{{in instantiation of function template specialization}}
208+
209+
}
210+
}
211+
}
212+
180213
#endif

0 commit comments

Comments
 (0)