Skip to content

Commit f1018e7

Browse files
committed
[Clang] Functions called in discarded statements should not be instantiated
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 f1018e7

File tree

6 files changed

+60
-29
lines changed

6 files changed

+60
-29
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/Sema.h

Lines changed: 3 additions & 2 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
};

clang/lib/Sema/SemaCoroutine.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -699,6 +699,9 @@ bool Sema::ActOnCoroutineBodyStart(Scope *SC, SourceLocation KWLoc,
699699
// Ignore previous expr evaluation contexts.
700700
EnterExpressionEvaluationContext PotentiallyEvaluated(
701701
*this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
702+
703+
ExprEvalContexts.back().InDiscardedStatement = false;
704+
702705
if (!checkCoroutineContext(*this, KWLoc, Keyword))
703706
return false;
704707
auto *ScopeInfo = getCurFunction();

clang/lib/Sema/SemaDecl.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15889,6 +15889,9 @@ Decl *Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, Decl *D,
1588915889
ExprEvalContexts.back().InImmediateEscalatingFunctionContext =
1589015890
getLangOpts().CPlusPlus20 && FD->isImmediateEscalating();
1589115891

15892+
// A function that is not a lambda is never in a discarded statement
15893+
ExprEvalContexts.back().InDiscardedStatement = false;
15894+
1589215895
// Check for defining attributes before the check for redefinition.
1589315896
if (const auto *Attr = FD->getAttr<AliasAttr>()) {
1589415897
Diag(Attr->getLocation(), diag::err_alias_is_definition) << FD << 0;

clang/lib/Sema/SemaExpr.cpp

Lines changed: 17 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -18360,35 +18360,23 @@ enum class OdrUseContext {
1836018360
/// Are we within a context in which references to resolved functions or to
1836118361
/// variables result in odr-use?
1836218362
static OdrUseContext isOdrUseContext(Sema &SemaRef) {
18363-
OdrUseContext Result;
18363+
const Sema::ExpressionEvaluationContextRecord &Context =
18364+
SemaRef.currentEvaluationContext();
1836418365

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;
18376-
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-
}
18366+
if (Context.isUnevaluated())
18367+
return OdrUseContext::None;
1838718368

1838818369
if (SemaRef.CurContext->isDependentContext())
1838918370
return OdrUseContext::Dependent;
1839018371

18391-
return Result;
18372+
if (Context.isDiscardedStatementContext())
18373+
return OdrUseContext::FormallyOdrUsed;
18374+
18375+
else if (Context.Context ==
18376+
Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed)
18377+
return OdrUseContext::FormallyOdrUsed;
18378+
18379+
return OdrUseContext::Used;
1839218380
}
1839318381

1839418382
static bool isImplicitlyDefinableConstexprFunction(FunctionDecl *Func) {
@@ -20355,9 +20343,11 @@ MarkExprReferenced(Sema &SemaRef, SourceLocation Loc, Decl *D, Expr *E,
2035520343
}
2035620344

2035720345
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.
20346+
// [basic.def.odr] (CWG 1614)
20347+
// A function is named by an expression or conversion [...]
20348+
// unless it is a pure virtual function and either the expression is not an
20349+
// id-expression naming the function with an explicitly qualified name or
20350+
// the expression forms a pointer to member
2036120351
bool OdrUse = true;
2036220352
if (const CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(E->getDecl()))
2036320353
if (Method->isVirtual() &&

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)