Skip to content

Commit f1f1b60

Browse files
committed
Implement CWG2631
Implement https://cplusplus.github.io/CWG/issues/2631.html. Immediate calls in default arguments and defaults members are not evaluated. Instead, we evaluate them when constructing a `CXXDefaultArgExpr`/`BuildCXXDefaultInitExpr`. The immediate calls are executed by doing a transform on the initializing expression. Note that lambdas are not considering subexpressions so we do not need to transform them. As a result of this patch, unused default member initializers are not considered odr-used, and errors about members binding to local variables in an outer scope only surface at the point where a constructor is defined. Reviewed By: aaron.ballman, #clang-language-wg Differential Revision: https://reviews.llvm.org/D136554
1 parent 7476d59 commit f1f1b60

27 files changed

+999
-156
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,11 @@ C++ Language Changes in Clang
645645
- Implemented DR2358 allowing init captures in lambdas in default arguments.
646646
- implemented `DR2654 <https://wg21.link/cwg2654>`_ which undeprecates
647647
all compound assignements operations on volatile qualified variables.
648+
- Implemented DR2631. Invalid ``consteval`` calls in default arguments and default
649+
member initializers are diagnosed when and if the default is used.
650+
This Fixes `Issue 56379 <https://github.com/llvm/llvm-project/issues/56379>`_
651+
and changes the value of ``std::source_location::current()``
652+
used in default parameters calls compared to previous versions of Clang.
648653

649654
C++20 Feature Support
650655
^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/ExprCXX.h

Lines changed: 84 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1244,8 +1244,12 @@ class CXXThrowExpr : public Expr {
12441244
/// This wraps up a function call argument that was created from the
12451245
/// corresponding parameter's default argument, when the call did not
12461246
/// explicitly supply arguments for all of the parameters.
1247-
class CXXDefaultArgExpr final : public Expr {
1247+
class CXXDefaultArgExpr final
1248+
: public Expr,
1249+
private llvm::TrailingObjects<CXXDefaultArgExpr, Expr *> {
12481250
friend class ASTStmtReader;
1251+
friend class ASTReader;
1252+
friend TrailingObjects;
12491253

12501254
/// The parameter whose default is being used.
12511255
ParmVarDecl *Param;
@@ -1254,7 +1258,7 @@ class CXXDefaultArgExpr final : public Expr {
12541258
DeclContext *UsedContext;
12551259

12561260
CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *Param,
1257-
DeclContext *UsedContext)
1261+
Expr *RewrittenExpr, DeclContext *UsedContext)
12581262
: Expr(SC,
12591263
Param->hasUnparsedDefaultArg()
12601264
? Param->getType().getNonReferenceType()
@@ -1263,28 +1267,58 @@ class CXXDefaultArgExpr final : public Expr {
12631267
Param->getDefaultArg()->getObjectKind()),
12641268
Param(Param), UsedContext(UsedContext) {
12651269
CXXDefaultArgExprBits.Loc = Loc;
1270+
CXXDefaultArgExprBits.HasRewrittenInit = RewrittenExpr != nullptr;
1271+
if (RewrittenExpr)
1272+
*getTrailingObjects<Expr *>() = RewrittenExpr;
12661273
setDependence(computeDependence(this));
12671274
}
12681275

1276+
CXXDefaultArgExpr(EmptyShell Empty, bool HasRewrittenInit)
1277+
: Expr(CXXDefaultArgExprClass, Empty) {
1278+
CXXDefaultArgExprBits.HasRewrittenInit = HasRewrittenInit;
1279+
}
1280+
1281+
size_t numTrailingObjects() const {
1282+
return CXXDefaultArgExprBits.HasRewrittenInit;
1283+
}
1284+
12691285
public:
1270-
CXXDefaultArgExpr(EmptyShell Empty) : Expr(CXXDefaultArgExprClass, Empty) {}
1286+
static CXXDefaultArgExpr *CreateEmpty(const ASTContext &C,
1287+
bool HasRewrittenInit);
12711288

12721289
// \p Param is the parameter whose default argument is used by this
12731290
// expression.
12741291
static CXXDefaultArgExpr *Create(const ASTContext &C, SourceLocation Loc,
1275-
ParmVarDecl *Param,
1276-
DeclContext *UsedContext) {
1277-
return new (C)
1278-
CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param, UsedContext);
1279-
}
1280-
1292+
ParmVarDecl *Param, Expr *RewrittenExpr,
1293+
DeclContext *UsedContext);
12811294
// Retrieve the parameter that the argument was created from.
12821295
const ParmVarDecl *getParam() const { return Param; }
12831296
ParmVarDecl *getParam() { return Param; }
12841297

1285-
// Retrieve the actual argument to the function call.
1286-
const Expr *getExpr() const { return getParam()->getDefaultArg(); }
1287-
Expr *getExpr() { return getParam()->getDefaultArg(); }
1298+
bool hasRewrittenInit() const {
1299+
return CXXDefaultArgExprBits.HasRewrittenInit;
1300+
}
1301+
1302+
// Retrieve the argument to the function call.
1303+
Expr *getExpr();
1304+
const Expr *getExpr() const {
1305+
return const_cast<CXXDefaultArgExpr *>(this)->getExpr();
1306+
}
1307+
1308+
Expr *getRewrittenExpr() {
1309+
return hasRewrittenInit() ? *getTrailingObjects<Expr *>() : nullptr;
1310+
}
1311+
1312+
const Expr *getRewrittenExpr() const {
1313+
return const_cast<CXXDefaultArgExpr *>(this)->getRewrittenExpr();
1314+
}
1315+
1316+
// Retrieve the rewritten init expression (for an init expression containing
1317+
// immediate calls) with the top level FullExpr and ConstantExpr stripped off.
1318+
Expr *getAdjustedRewrittenExpr();
1319+
const Expr *getAdjustedRewrittenExpr() const {
1320+
return const_cast<CXXDefaultArgExpr *>(this)->getAdjustedRewrittenExpr();
1321+
}
12881322

12891323
const DeclContext *getUsedContext() const { return UsedContext; }
12901324
DeclContext *getUsedContext() { return UsedContext; }
@@ -1321,41 +1355,67 @@ class CXXDefaultArgExpr final : public Expr {
13211355
/// is implicitly used in a mem-initializer-list in a constructor
13221356
/// (C++11 [class.base.init]p8) or in aggregate initialization
13231357
/// (C++1y [dcl.init.aggr]p7).
1324-
class CXXDefaultInitExpr : public Expr {
1325-
friend class ASTReader;
1326-
friend class ASTStmtReader;
1358+
class CXXDefaultInitExpr final
1359+
: public Expr,
1360+
private llvm::TrailingObjects<CXXDefaultArgExpr, Expr *> {
13271361

1362+
friend class ASTStmtReader;
1363+
friend class ASTReader;
1364+
friend TrailingObjects;
13281365
/// The field whose default is being used.
13291366
FieldDecl *Field;
13301367

13311368
/// The context where the default initializer expression was used.
13321369
DeclContext *UsedContext;
13331370

13341371
CXXDefaultInitExpr(const ASTContext &Ctx, SourceLocation Loc,
1335-
FieldDecl *Field, QualType Ty, DeclContext *UsedContext);
1372+
FieldDecl *Field, QualType Ty, DeclContext *UsedContext,
1373+
Expr *RewrittenInitExpr);
1374+
1375+
CXXDefaultInitExpr(EmptyShell Empty, bool HasRewrittenInit)
1376+
: Expr(CXXDefaultInitExprClass, Empty) {
1377+
CXXDefaultInitExprBits.HasRewrittenInit = HasRewrittenInit;
1378+
}
13361379

1337-
CXXDefaultInitExpr(EmptyShell Empty) : Expr(CXXDefaultInitExprClass, Empty) {}
1380+
size_t numTrailingObjects() const {
1381+
return CXXDefaultInitExprBits.HasRewrittenInit;
1382+
}
13381383

13391384
public:
1385+
static CXXDefaultInitExpr *CreateEmpty(const ASTContext &C,
1386+
bool HasRewrittenInit);
13401387
/// \p Field is the non-static data member whose default initializer is used
13411388
/// by this expression.
13421389
static CXXDefaultInitExpr *Create(const ASTContext &Ctx, SourceLocation Loc,
1343-
FieldDecl *Field, DeclContext *UsedContext) {
1344-
return new (Ctx) CXXDefaultInitExpr(Ctx, Loc, Field, Field->getType(), UsedContext);
1390+
FieldDecl *Field, DeclContext *UsedContext,
1391+
Expr *RewrittenInitExpr);
1392+
1393+
bool hasRewrittenInit() const {
1394+
return CXXDefaultInitExprBits.HasRewrittenInit;
13451395
}
13461396

13471397
/// Get the field whose initializer will be used.
13481398
FieldDecl *getField() { return Field; }
13491399
const FieldDecl *getField() const { return Field; }
13501400

13511401
/// Get the initialization expression that will be used.
1402+
Expr *getExpr();
13521403
const Expr *getExpr() const {
1353-
assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
1354-
return Field->getInClassInitializer();
1404+
return const_cast<CXXDefaultInitExpr *>(this)->getExpr();
1405+
}
1406+
1407+
/// Retrieve the initializing expression with evaluated immediate calls, if
1408+
/// any.
1409+
const Expr *getRewrittenExpr() const {
1410+
assert(hasRewrittenInit() && "expected a rewritten init expression");
1411+
return *getTrailingObjects<Expr *>();
13551412
}
1356-
Expr *getExpr() {
1357-
assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
1358-
return Field->getInClassInitializer();
1413+
1414+
/// Retrieve the initializing expression with evaluated immediate calls, if
1415+
/// any.
1416+
Expr *getRewrittenExpr() {
1417+
assert(hasRewrittenInit() && "expected a rewritten init expression");
1418+
return *getTrailingObjects<Expr *>();
13591419
}
13601420

13611421
const DeclContext *getUsedContext() const { return UsedContext; }

clang/include/clang/AST/Stmt.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,9 @@ class alignas(void *) Stmt {
690690

691691
unsigned : NumExprBits;
692692

693+
/// Whether this CXXDefaultArgExpr rewrote its argument and stores a copy.
694+
unsigned HasRewrittenInit : 1;
695+
693696
/// The location where the default argument expression was used.
694697
SourceLocation Loc;
695698
};
@@ -700,6 +703,10 @@ class alignas(void *) Stmt {
700703

701704
unsigned : NumExprBits;
702705

706+
/// Whether this CXXDefaultInitExprBitfields rewrote its argument and stores
707+
/// a copy.
708+
unsigned HasRewrittenInit : 1;
709+
703710
/// The location where the default initializer expression was used.
704711
SourceLocation Loc;
705712
};

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2646,6 +2646,10 @@ def err_invalid_consteval_take_address : Error<
26462646
" of an immediate invocation">;
26472647
def err_invalid_consteval_call : Error<
26482648
"call to consteval function %q0 is not a constant expression">;
2649+
def note_invalid_consteval_initializer : Note<
2650+
"in the default initalizer of %0">;
2651+
def note_invalid_consteval_initializer_here : Note<
2652+
"initialized here %0">;
26492653
def err_invalid_consteval_decl_kind : Error<
26502654
"%0 cannot be declared consteval">;
26512655
def err_invalid_constexpr : Error<

clang/include/clang/Sema/Sema.h

Lines changed: 83 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1330,6 +1330,25 @@ class Sema final {
13301330
bool InDiscardedStatement;
13311331
bool InImmediateFunctionContext;
13321332

1333+
bool IsCurrentlyCheckingDefaultArgumentOrInitializer = false;
1334+
1335+
// When evaluating immediate functions in the initializer of a default
1336+
// argument or default member initializer, this is the declaration whose
1337+
// default initializer is being evaluated and the location of the call
1338+
// or constructor definition.
1339+
struct InitializationContext {
1340+
InitializationContext(SourceLocation Loc, ValueDecl *Decl,
1341+
DeclContext *Context)
1342+
: Loc(Loc), Decl(Decl), Context(Context) {
1343+
assert(Decl && Context && "invalid initialization context");
1344+
}
1345+
1346+
SourceLocation Loc;
1347+
ValueDecl *Decl = nullptr;
1348+
DeclContext *Context = nullptr;
1349+
};
1350+
llvm::Optional<InitializationContext> DelayedDefaultInitializationContext;
1351+
13331352
ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context,
13341353
unsigned NumCleanupObjects,
13351354
CleanupInfo ParentCleanup,
@@ -6208,19 +6227,22 @@ class Sema final {
62086227
bool IsStdInitListInitialization, bool RequiresZeroInit,
62096228
unsigned ConstructKind, SourceRange ParenRange);
62106229

6230+
ExprResult ConvertMemberDefaultInitExpression(FieldDecl *FD, Expr *InitExpr,
6231+
SourceLocation InitLoc);
6232+
62116233
ExprResult BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field);
62126234

62136235

62146236
/// Instantiate or parse a C++ default argument expression as necessary.
62156237
/// Return true on error.
62166238
bool CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
6217-
ParmVarDecl *Param);
6239+
ParmVarDecl *Param, Expr *Init = nullptr,
6240+
bool SkipImmediateInvocations = true);
62186241

62196242
/// BuildCXXDefaultArgExpr - Creates a CXXDefaultArgExpr, instantiating
62206243
/// the default expr if needed.
6221-
ExprResult BuildCXXDefaultArgExpr(SourceLocation CallLoc,
6222-
FunctionDecl *FD,
6223-
ParmVarDecl *Param);
6244+
ExprResult BuildCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
6245+
ParmVarDecl *Param, Expr *Init = nullptr);
62246246

62256247
/// FinalizeVarWithDestructor - Prepare for calling destructor on the
62266248
/// constructed variable.
@@ -9627,6 +9649,63 @@ class Sema final {
96279649
return ExprEvalContexts.back().isImmediateFunctionContext();
96289650
}
96299651

9652+
bool isCheckingDefaultArgumentOrInitializer() const {
9653+
assert(!ExprEvalContexts.empty() &&
9654+
"Must be in an expression evaluation context");
9655+
const ExpressionEvaluationContextRecord &Ctx = ExprEvalContexts.back();
9656+
return (Ctx.Context ==
9657+
ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed) ||
9658+
Ctx.IsCurrentlyCheckingDefaultArgumentOrInitializer;
9659+
}
9660+
9661+
bool isCheckingDefaultArgumentOrInitializerOfOuterEntity() const {
9662+
assert(!ExprEvalContexts.empty() &&
9663+
"Must be in an expression evaluation context");
9664+
for (const auto &Ctx : llvm::reverse(ExprEvalContexts)) {
9665+
if ((Ctx.Context ==
9666+
ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed) ||
9667+
Ctx.IsCurrentlyCheckingDefaultArgumentOrInitializer)
9668+
return true;
9669+
if (Ctx.isConstantEvaluated() || Ctx.isImmediateFunctionContext() ||
9670+
Ctx.isUnevaluated())
9671+
return false;
9672+
}
9673+
return false;
9674+
}
9675+
9676+
llvm::Optional<ExpressionEvaluationContextRecord::InitializationContext>
9677+
InnermostDeclarationWithDelayedImmediateInvocations() const {
9678+
assert(!ExprEvalContexts.empty() &&
9679+
"Must be in an expression evaluation context");
9680+
for (const auto &Ctx : llvm::reverse(ExprEvalContexts)) {
9681+
if (Ctx.Context == ExpressionEvaluationContext::PotentiallyEvaluated &&
9682+
Ctx.DelayedDefaultInitializationContext)
9683+
return Ctx.DelayedDefaultInitializationContext;
9684+
if (Ctx.isConstantEvaluated() || Ctx.isImmediateFunctionContext() ||
9685+
Ctx.isUnevaluated())
9686+
break;
9687+
}
9688+
return std::nullopt;
9689+
}
9690+
9691+
llvm::Optional<ExpressionEvaluationContextRecord::InitializationContext>
9692+
OutermostDeclarationWithDelayedImmediateInvocations() const {
9693+
assert(!ExprEvalContexts.empty() &&
9694+
"Must be in an expression evaluation context");
9695+
llvm::Optional<ExpressionEvaluationContextRecord::InitializationContext>
9696+
Res;
9697+
for (auto &Ctx : llvm::reverse(ExprEvalContexts)) {
9698+
if (Ctx.Context == ExpressionEvaluationContext::PotentiallyEvaluated &&
9699+
!Ctx.DelayedDefaultInitializationContext && Res)
9700+
break;
9701+
if (Ctx.isConstantEvaluated() || Ctx.isImmediateFunctionContext() ||
9702+
Ctx.isUnevaluated())
9703+
break;
9704+
Res = Ctx.DelayedDefaultInitializationContext;
9705+
}
9706+
return Res;
9707+
}
9708+
96309709
/// RAII class used to determine whether SFINAE has
96319710
/// trapped any errors that occur during template argument
96329711
/// deduction.

clang/lib/AST/ASTImporter.cpp

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7687,9 +7687,16 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
76877687
if (Error Err = ImportDefaultArgOfParmVarDecl(*FromParam, ToParam))
76887688
return std::move(Err);
76897689
}
7690-
7690+
Expr *RewrittenInit = nullptr;
7691+
if (E->hasRewrittenInit()) {
7692+
ExpectedExpr ExprOrErr = import(E->getExpr());
7693+
if (!ExprOrErr)
7694+
return ExprOrErr.takeError();
7695+
RewrittenInit = ExprOrErr.get();
7696+
}
76917697
return CXXDefaultArgExpr::Create(Importer.getToContext(), *ToUsedLocOrErr,
7692-
*ToParamOrErr, *UsedContextOrErr);
7698+
*ToParamOrErr, RewrittenInit,
7699+
*UsedContextOrErr);
76937700
}
76947701

76957702
ExpectedStmt
@@ -8381,8 +8388,16 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
83818388
ToField->setInClassInitializer(*ToInClassInitializerOrErr);
83828389
}
83838390

8391+
Expr *RewrittenInit = nullptr;
8392+
if (E->hasRewrittenInit()) {
8393+
ExpectedExpr ExprOrErr = import(E->getExpr());
8394+
if (!ExprOrErr)
8395+
return ExprOrErr.takeError();
8396+
RewrittenInit = ExprOrErr.get();
8397+
}
8398+
83848399
return CXXDefaultInitExpr::Create(Importer.getToContext(), *ToBeginLocOrErr,
8385-
ToField, *UsedContextOrErr);
8400+
ToField, *UsedContextOrErr, RewrittenInit);
83868401
}
83878402

83888403
ExpectedStmt ASTNodeImporter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) {

clang/lib/AST/Decl.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2887,8 +2887,7 @@ Expr *ParmVarDecl::getDefaultArg() {
28872887

28882888
Expr *Arg = getInit();
28892889
if (auto *E = dyn_cast_or_null<FullExpr>(Arg))
2890-
if (!isa<ConstantExpr>(E))
2891-
return E->getSubExpr();
2890+
return E->getSubExpr();
28922891

28932892
return Arg;
28942893
}

0 commit comments

Comments
 (0)