Skip to content

Commit 26fa17e

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 451c017 commit 26fa17e

25 files changed

+843
-156
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,8 +616,16 @@ C++ Language Changes in Clang
616616
conforming GNU extensions. Projects incompatible with C++17 can add
617617
``-std=gnu++14`` to their build settings to restore the previous behaviour.
618618
- Implemented DR2358 allowing init captures in lambdas in default arguments.
619+
<<<<<<< HEAD
619620
- implemented `DR2654 <https://wg21.link/cwg2654>`_ which undeprecates
620621
all compound assignements operations on volatile qualified variables.
622+
=======
623+
- Implemented DR2631. Invalid ``consteval`` calls in default arguments and default
624+
member initializers are diagnosed when and if the default is used.
625+
This Fixes `Issue 56379 <https://github.com/llvm/llvm-project/issues/56379>`_
626+
and changes the value of ``std::source_location::current()``
627+
used in default parameters calls compared to previous versions of Clang.
628+
>>>>>>> 7c4d4089e1fd (Implement CWG2631)
621629

622630
C++20 Feature Support
623631
^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/AST/ExprCXX.h

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

12511255
/// The parameter whose default is being used.
12521256
ParmVarDecl *Param;
@@ -1255,7 +1259,7 @@ class CXXDefaultArgExpr final : public Expr {
12551259
DeclContext *UsedContext;
12561260

12571261
CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *Param,
1258-
DeclContext *UsedContext)
1262+
Expr *RewrittenExpr, DeclContext *UsedContext)
12591263
: Expr(SC,
12601264
Param->hasUnparsedDefaultArg()
12611265
? Param->getType().getNonReferenceType()
@@ -1264,28 +1268,58 @@ class CXXDefaultArgExpr final : public Expr {
12641268
Param->getDefaultArg()->getObjectKind()),
12651269
Param(Param), UsedContext(UsedContext) {
12661270
CXXDefaultArgExprBits.Loc = Loc;
1271+
CXXDefaultArgExprBits.HasRewrittenInit = RewrittenExpr != nullptr;
1272+
if (RewrittenExpr)
1273+
*getTrailingObjects<Expr *>() = RewrittenExpr;
12671274
setDependence(computeDependence(this));
12681275
}
12691276

1277+
CXXDefaultArgExpr(EmptyShell Empty, bool HasRewrittenInit)
1278+
: Expr(CXXDefaultArgExprClass, Empty) {
1279+
CXXDefaultArgExprBits.HasRewrittenInit = HasRewrittenInit;
1280+
}
1281+
1282+
size_t numTrailingObjects() const {
1283+
return CXXDefaultArgExprBits.HasRewrittenInit;
1284+
}
1285+
12701286
public:
1271-
CXXDefaultArgExpr(EmptyShell Empty) : Expr(CXXDefaultArgExprClass, Empty) {}
1287+
static CXXDefaultArgExpr *CreateEmpty(const ASTContext &C,
1288+
bool HasRewrittenInit);
12721289

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

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

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

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

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

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

1338-
CXXDefaultInitExpr(EmptyShell Empty) : Expr(CXXDefaultInitExprClass, Empty) {}
1381+
size_t numTrailingObjects() const {
1382+
return CXXDefaultInitExprBits.HasRewrittenInit;
1383+
}
13391384

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

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

13521402
/// Get the initialization expression that will be used.
1403+
Expr *getExpr();
13531404
const Expr *getExpr() const {
1354-
assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
1355-
return Field->getInClassInitializer();
1405+
return const_cast<CXXDefaultInitExpr *>(this)->getExpr();
1406+
}
1407+
1408+
/// Retrieve the initializing expression with evaluated immediate calls, if
1409+
/// any.
1410+
const Expr *getRewrittenExpr() const {
1411+
assert(hasRewrittenInit() && "expected a rewritten init expression");
1412+
return *getTrailingObjects<Expr *>();
13561413
}
1357-
Expr *getExpr() {
1358-
assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
1359-
return Field->getInClassInitializer();
1414+
1415+
/// Retrieve the initializing expression with evaluated immediate calls, if
1416+
/// any.
1417+
Expr *getRewrittenExpr() {
1418+
assert(hasRewrittenInit() && "expected a rewritten init expression");
1419+
return *getTrailingObjects<Expr *>();
13601420
}
13611421

13621422
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,
@@ -6210,19 +6229,22 @@ class Sema final {
62106229
bool IsStdInitListInitialization, bool RequiresZeroInit,
62116230
unsigned ConstructKind, SourceRange ParenRange);
62126231

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

62156237

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

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

62276249
/// FinalizeVarWithDestructor - Prepare for calling destructor on the
62286250
/// constructed variable.
@@ -9626,6 +9648,63 @@ class Sema final {
96269648
return ExprEvalContexts.back().isImmediateFunctionContext();
96279649
}
96289650

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

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

28942893
return Arg;
28952894
}

0 commit comments

Comments
 (0)