Skip to content

Commit ca61961

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, rupprecht Differential Revision: https://reviews.llvm.org/D136554
1 parent eda8e99 commit ca61961

28 files changed

+1133
-157
lines changed

clang/docs/ReleaseNotes.rst

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

675680
C++20 Feature Support
676681
^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/ExprCXX.h

Lines changed: 76 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,54 @@ 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+
12691281
public:
1270-
CXXDefaultArgExpr(EmptyShell Empty) : Expr(CXXDefaultArgExprClass, Empty) {}
1282+
static CXXDefaultArgExpr *CreateEmpty(const ASTContext &C,
1283+
bool HasRewrittenInit);
12711284

12721285
// \p Param is the parameter whose default argument is used by this
12731286
// expression.
12741287
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-
1288+
ParmVarDecl *Param, Expr *RewrittenExpr,
1289+
DeclContext *UsedContext);
12811290
// Retrieve the parameter that the argument was created from.
12821291
const ParmVarDecl *getParam() const { return Param; }
12831292
ParmVarDecl *getParam() { return Param; }
12841293

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

12891319
const DeclContext *getUsedContext() const { return UsedContext; }
12901320
DeclContext *getUsedContext() { return UsedContext; }
@@ -1321,41 +1351,63 @@ class CXXDefaultArgExpr final : public Expr {
13211351
/// is implicitly used in a mem-initializer-list in a constructor
13221352
/// (C++11 [class.base.init]p8) or in aggregate initialization
13231353
/// (C++1y [dcl.init.aggr]p7).
1324-
class CXXDefaultInitExpr : public Expr {
1325-
friend class ASTReader;
1326-
friend class ASTStmtReader;
1354+
class CXXDefaultInitExpr final
1355+
: public Expr,
1356+
private llvm::TrailingObjects<CXXDefaultInitExpr, Expr *> {
13271357

1358+
friend class ASTStmtReader;
1359+
friend class ASTReader;
1360+
friend TrailingObjects;
13281361
/// The field whose default is being used.
13291362
FieldDecl *Field;
13301363

13311364
/// The context where the default initializer expression was used.
13321365
DeclContext *UsedContext;
13331366

13341367
CXXDefaultInitExpr(const ASTContext &Ctx, SourceLocation Loc,
1335-
FieldDecl *Field, QualType Ty, DeclContext *UsedContext);
1368+
FieldDecl *Field, QualType Ty, DeclContext *UsedContext,
1369+
Expr *RewrittenInitExpr);
13361370

1337-
CXXDefaultInitExpr(EmptyShell Empty) : Expr(CXXDefaultInitExprClass, Empty) {}
1371+
CXXDefaultInitExpr(EmptyShell Empty, bool HasRewrittenInit)
1372+
: Expr(CXXDefaultInitExprClass, Empty) {
1373+
CXXDefaultInitExprBits.HasRewrittenInit = HasRewrittenInit;
1374+
}
13381375

13391376
public:
1377+
static CXXDefaultInitExpr *CreateEmpty(const ASTContext &C,
1378+
bool HasRewrittenInit);
13401379
/// \p Field is the non-static data member whose default initializer is used
13411380
/// by this expression.
13421381
static CXXDefaultInitExpr *Create(const ASTContext &Ctx, SourceLocation Loc,
1343-
FieldDecl *Field, DeclContext *UsedContext) {
1344-
return new (Ctx) CXXDefaultInitExpr(Ctx, Loc, Field, Field->getType(), UsedContext);
1382+
FieldDecl *Field, DeclContext *UsedContext,
1383+
Expr *RewrittenInitExpr);
1384+
1385+
bool hasRewrittenInit() const {
1386+
return CXXDefaultInitExprBits.HasRewrittenInit;
13451387
}
13461388

13471389
/// Get the field whose initializer will be used.
13481390
FieldDecl *getField() { return Field; }
13491391
const FieldDecl *getField() const { return Field; }
13501392

13511393
/// Get the initialization expression that will be used.
1394+
Expr *getExpr();
13521395
const Expr *getExpr() const {
1353-
assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
1354-
return Field->getInClassInitializer();
1396+
return const_cast<CXXDefaultInitExpr *>(this)->getExpr();
13551397
}
1356-
Expr *getExpr() {
1357-
assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
1358-
return Field->getInClassInitializer();
1398+
1399+
/// Retrieve the initializing expression with evaluated immediate calls, if
1400+
/// any.
1401+
const Expr *getRewrittenExpr() const {
1402+
assert(hasRewrittenInit() && "expected a rewritten init expression");
1403+
return *getTrailingObjects<Expr *>();
1404+
}
1405+
1406+
/// Retrieve the initializing expression with evaluated immediate calls, if
1407+
/// any.
1408+
Expr *getRewrittenExpr() {
1409+
assert(hasRewrittenInit() && "expected a rewritten init expression");
1410+
return *getTrailingObjects<Expr *>();
13591411
}
13601412

13611413
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: 68 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,
@@ -6211,19 +6230,22 @@ class Sema final {
62116230
bool IsStdInitListInitialization, bool RequiresZeroInit,
62126231
unsigned ConstructKind, SourceRange ParenRange);
62136232

6233+
ExprResult ConvertMemberDefaultInitExpression(FieldDecl *FD, Expr *InitExpr,
6234+
SourceLocation InitLoc);
6235+
62146236
ExprResult BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field);
62156237

62166238

62176239
/// Instantiate or parse a C++ default argument expression as necessary.
62186240
/// Return true on error.
62196241
bool CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
6220-
ParmVarDecl *Param);
6242+
ParmVarDecl *Param, Expr *Init = nullptr,
6243+
bool SkipImmediateInvocations = true);
62216244

62226245
/// BuildCXXDefaultArgExpr - Creates a CXXDefaultArgExpr, instantiating
62236246
/// the default expr if needed.
6224-
ExprResult BuildCXXDefaultArgExpr(SourceLocation CallLoc,
6225-
FunctionDecl *FD,
6226-
ParmVarDecl *Param);
6247+
ExprResult BuildCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
6248+
ParmVarDecl *Param, Expr *Init = nullptr);
62276249

62286250
/// FinalizeVarWithDestructor - Prepare for calling destructor on the
62296251
/// constructed variable.
@@ -9636,6 +9658,48 @@ class Sema final {
96369658
return ExprEvalContexts.back().isImmediateFunctionContext();
96379659
}
96389660

9661+
bool isCheckingDefaultArgumentOrInitializer() const {
9662+
assert(!ExprEvalContexts.empty() &&
9663+
"Must be in an expression evaluation context");
9664+
const ExpressionEvaluationContextRecord &Ctx = ExprEvalContexts.back();
9665+
return (Ctx.Context ==
9666+
ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed) ||
9667+
Ctx.IsCurrentlyCheckingDefaultArgumentOrInitializer;
9668+
}
9669+
9670+
llvm::Optional<ExpressionEvaluationContextRecord::InitializationContext>
9671+
InnermostDeclarationWithDelayedImmediateInvocations() const {
9672+
assert(!ExprEvalContexts.empty() &&
9673+
"Must be in an expression evaluation context");
9674+
for (const auto &Ctx : llvm::reverse(ExprEvalContexts)) {
9675+
if (Ctx.Context == ExpressionEvaluationContext::PotentiallyEvaluated &&
9676+
Ctx.DelayedDefaultInitializationContext)
9677+
return Ctx.DelayedDefaultInitializationContext;
9678+
if (Ctx.isConstantEvaluated() || Ctx.isImmediateFunctionContext() ||
9679+
Ctx.isUnevaluated())
9680+
break;
9681+
}
9682+
return std::nullopt;
9683+
}
9684+
9685+
llvm::Optional<ExpressionEvaluationContextRecord::InitializationContext>
9686+
OutermostDeclarationWithDelayedImmediateInvocations() const {
9687+
assert(!ExprEvalContexts.empty() &&
9688+
"Must be in an expression evaluation context");
9689+
llvm::Optional<ExpressionEvaluationContextRecord::InitializationContext>
9690+
Res;
9691+
for (auto &Ctx : llvm::reverse(ExprEvalContexts)) {
9692+
if (Ctx.Context == ExpressionEvaluationContext::PotentiallyEvaluated &&
9693+
!Ctx.DelayedDefaultInitializationContext && Res)
9694+
break;
9695+
if (Ctx.isConstantEvaluated() || Ctx.isImmediateFunctionContext() ||
9696+
Ctx.isUnevaluated())
9697+
break;
9698+
Res = Ctx.DelayedDefaultInitializationContext;
9699+
}
9700+
return Res;
9701+
}
9702+
96399703
/// RAII class used to determine whether SFINAE has
96409704
/// trapped any errors that occur during template argument
96419705
/// 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->getRewrittenExpr());
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->getRewrittenExpr());
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
@@ -2897,8 +2897,7 @@ Expr *ParmVarDecl::getDefaultArg() {
28972897

28982898
Expr *Arg = getInit();
28992899
if (auto *E = dyn_cast_or_null<FullExpr>(Arg))
2900-
if (!isa<ConstantExpr>(E))
2901-
return E->getSubExpr();
2900+
return E->getSubExpr();
29022901

29032902
return Arg;
29042903
}

0 commit comments

Comments
 (0)