[clang] ca61961 - Implement CWG2631
Corentin Jabot via cfe-commits
cfe-commits at lists.llvm.org
Sun Jan 8 01:35:34 PST 2023
Author: Corentin Jabot
Date: 2023-01-08T10:35:26+01:00
New Revision: ca619613801233ef2def8c3cc7d311d5ed0033cb
URL: https://github.com/llvm/llvm-project/commit/ca619613801233ef2def8c3cc7d311d5ed0033cb
DIFF: https://github.com/llvm/llvm-project/commit/ca619613801233ef2def8c3cc7d311d5ed0033cb.diff
LOG: 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
Added:
clang/test/CodeGenCXX/default-arguments-with-immediate.cpp
clang/test/CodeGenCXX/meminit-initializers-odr.cpp
clang/test/PCH/default-argument-with-immediate-calls.cpp
clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/AST/ExprCXX.h
clang/include/clang/AST/Stmt.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/AST/ASTImporter.cpp
clang/lib/AST/Decl.cpp
clang/lib/AST/ExprCXX.cpp
clang/lib/Parse/ParseCXXInlineMethods.cpp
clang/lib/Parse/ParseDeclCXX.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaTemplateInstantiate.cpp
clang/lib/Sema/TreeTransform.h
clang/lib/Sema/UsedDeclVisitor.h
clang/lib/Serialization/ASTReaderStmt.cpp
clang/lib/Serialization/ASTWriterStmt.cpp
clang/test/AST/ast-dump-records.cpp
clang/test/CXX/class/class.local/p1-0x.cpp
clang/test/CXX/drs/dr26xx.cpp
clang/test/CodeGenCXX/builtin-source-location.cpp
clang/test/SemaCXX/cxx11-default-member-initializers.cpp
clang/test/SemaCXX/source_location.cpp
clang/www/cxx_dr_status.html
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f2cfac238613c..91cb9049bcb4b 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -671,6 +671,11 @@ C++ Language Changes in Clang
- Implemented DR2358 allowing init captures in lambdas in default arguments.
- implemented `DR2654 <https://wg21.link/cwg2654>`_ which undeprecates
all compound assignements operations on volatile qualified variables.
+- Implemented DR2631. Invalid ``consteval`` calls in default arguments and default
+ member initializers are diagnosed when and if the default is used.
+ This Fixes `Issue 56379 <https://github.com/llvm/llvm-project/issues/56379>`_
+ and changes the value of ``std::source_location::current()``
+ used in default parameters calls compared to previous versions of Clang.
C++20 Feature Support
^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/AST/ExprCXX.h b/clang/include/clang/AST/ExprCXX.h
index c2f6751d62ede..a0bdf50f57ac0 100644
--- a/clang/include/clang/AST/ExprCXX.h
+++ b/clang/include/clang/AST/ExprCXX.h
@@ -1244,8 +1244,12 @@ class CXXThrowExpr : public Expr {
/// This wraps up a function call argument that was created from the
/// corresponding parameter's default argument, when the call did not
/// explicitly supply arguments for all of the parameters.
-class CXXDefaultArgExpr final : public Expr {
+class CXXDefaultArgExpr final
+ : public Expr,
+ private llvm::TrailingObjects<CXXDefaultArgExpr, Expr *> {
friend class ASTStmtReader;
+ friend class ASTReader;
+ friend TrailingObjects;
/// The parameter whose default is being used.
ParmVarDecl *Param;
@@ -1254,7 +1258,7 @@ class CXXDefaultArgExpr final : public Expr {
DeclContext *UsedContext;
CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *Param,
- DeclContext *UsedContext)
+ Expr *RewrittenExpr, DeclContext *UsedContext)
: Expr(SC,
Param->hasUnparsedDefaultArg()
? Param->getType().getNonReferenceType()
@@ -1263,28 +1267,54 @@ class CXXDefaultArgExpr final : public Expr {
Param->getDefaultArg()->getObjectKind()),
Param(Param), UsedContext(UsedContext) {
CXXDefaultArgExprBits.Loc = Loc;
+ CXXDefaultArgExprBits.HasRewrittenInit = RewrittenExpr != nullptr;
+ if (RewrittenExpr)
+ *getTrailingObjects<Expr *>() = RewrittenExpr;
setDependence(computeDependence(this));
}
+ CXXDefaultArgExpr(EmptyShell Empty, bool HasRewrittenInit)
+ : Expr(CXXDefaultArgExprClass, Empty) {
+ CXXDefaultArgExprBits.HasRewrittenInit = HasRewrittenInit;
+ }
+
public:
- CXXDefaultArgExpr(EmptyShell Empty) : Expr(CXXDefaultArgExprClass, Empty) {}
+ static CXXDefaultArgExpr *CreateEmpty(const ASTContext &C,
+ bool HasRewrittenInit);
// \p Param is the parameter whose default argument is used by this
// expression.
static CXXDefaultArgExpr *Create(const ASTContext &C, SourceLocation Loc,
- ParmVarDecl *Param,
- DeclContext *UsedContext) {
- return new (C)
- CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param, UsedContext);
- }
-
+ ParmVarDecl *Param, Expr *RewrittenExpr,
+ DeclContext *UsedContext);
// Retrieve the parameter that the argument was created from.
const ParmVarDecl *getParam() const { return Param; }
ParmVarDecl *getParam() { return Param; }
- // Retrieve the actual argument to the function call.
- const Expr *getExpr() const { return getParam()->getDefaultArg(); }
- Expr *getExpr() { return getParam()->getDefaultArg(); }
+ bool hasRewrittenInit() const {
+ return CXXDefaultArgExprBits.HasRewrittenInit;
+ }
+
+ // Retrieve the argument to the function call.
+ Expr *getExpr();
+ const Expr *getExpr() const {
+ return const_cast<CXXDefaultArgExpr *>(this)->getExpr();
+ }
+
+ Expr *getRewrittenExpr() {
+ return hasRewrittenInit() ? *getTrailingObjects<Expr *>() : nullptr;
+ }
+
+ const Expr *getRewrittenExpr() const {
+ return const_cast<CXXDefaultArgExpr *>(this)->getRewrittenExpr();
+ }
+
+ // Retrieve the rewritten init expression (for an init expression containing
+ // immediate calls) with the top level FullExpr and ConstantExpr stripped off.
+ Expr *getAdjustedRewrittenExpr();
+ const Expr *getAdjustedRewrittenExpr() const {
+ return const_cast<CXXDefaultArgExpr *>(this)->getAdjustedRewrittenExpr();
+ }
const DeclContext *getUsedContext() const { return UsedContext; }
DeclContext *getUsedContext() { return UsedContext; }
@@ -1321,10 +1351,13 @@ class CXXDefaultArgExpr final : public Expr {
/// is implicitly used in a mem-initializer-list in a constructor
/// (C++11 [class.base.init]p8) or in aggregate initialization
/// (C++1y [dcl.init.aggr]p7).
-class CXXDefaultInitExpr : public Expr {
- friend class ASTReader;
- friend class ASTStmtReader;
+class CXXDefaultInitExpr final
+ : public Expr,
+ private llvm::TrailingObjects<CXXDefaultInitExpr, Expr *> {
+ friend class ASTStmtReader;
+ friend class ASTReader;
+ friend TrailingObjects;
/// The field whose default is being used.
FieldDecl *Field;
@@ -1332,16 +1365,25 @@ class CXXDefaultInitExpr : public Expr {
DeclContext *UsedContext;
CXXDefaultInitExpr(const ASTContext &Ctx, SourceLocation Loc,
- FieldDecl *Field, QualType Ty, DeclContext *UsedContext);
+ FieldDecl *Field, QualType Ty, DeclContext *UsedContext,
+ Expr *RewrittenInitExpr);
- CXXDefaultInitExpr(EmptyShell Empty) : Expr(CXXDefaultInitExprClass, Empty) {}
+ CXXDefaultInitExpr(EmptyShell Empty, bool HasRewrittenInit)
+ : Expr(CXXDefaultInitExprClass, Empty) {
+ CXXDefaultInitExprBits.HasRewrittenInit = HasRewrittenInit;
+ }
public:
+ static CXXDefaultInitExpr *CreateEmpty(const ASTContext &C,
+ bool HasRewrittenInit);
/// \p Field is the non-static data member whose default initializer is used
/// by this expression.
static CXXDefaultInitExpr *Create(const ASTContext &Ctx, SourceLocation Loc,
- FieldDecl *Field, DeclContext *UsedContext) {
- return new (Ctx) CXXDefaultInitExpr(Ctx, Loc, Field, Field->getType(), UsedContext);
+ FieldDecl *Field, DeclContext *UsedContext,
+ Expr *RewrittenInitExpr);
+
+ bool hasRewrittenInit() const {
+ return CXXDefaultInitExprBits.HasRewrittenInit;
}
/// Get the field whose initializer will be used.
@@ -1349,13 +1391,23 @@ class CXXDefaultInitExpr : public Expr {
const FieldDecl *getField() const { return Field; }
/// Get the initialization expression that will be used.
+ Expr *getExpr();
const Expr *getExpr() const {
- assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
- return Field->getInClassInitializer();
+ return const_cast<CXXDefaultInitExpr *>(this)->getExpr();
}
- Expr *getExpr() {
- assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
- return Field->getInClassInitializer();
+
+ /// Retrieve the initializing expression with evaluated immediate calls, if
+ /// any.
+ const Expr *getRewrittenExpr() const {
+ assert(hasRewrittenInit() && "expected a rewritten init expression");
+ return *getTrailingObjects<Expr *>();
+ }
+
+ /// Retrieve the initializing expression with evaluated immediate calls, if
+ /// any.
+ Expr *getRewrittenExpr() {
+ assert(hasRewrittenInit() && "expected a rewritten init expression");
+ return *getTrailingObjects<Expr *>();
}
const DeclContext *getUsedContext() const { return UsedContext; }
diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h
index 619d85664476a..8781c70abfa26 100644
--- a/clang/include/clang/AST/Stmt.h
+++ b/clang/include/clang/AST/Stmt.h
@@ -690,6 +690,9 @@ class alignas(void *) Stmt {
unsigned : NumExprBits;
+ /// Whether this CXXDefaultArgExpr rewrote its argument and stores a copy.
+ unsigned HasRewrittenInit : 1;
+
/// The location where the default argument expression was used.
SourceLocation Loc;
};
@@ -700,6 +703,10 @@ class alignas(void *) Stmt {
unsigned : NumExprBits;
+ /// Whether this CXXDefaultInitExprBitfields rewrote its argument and stores
+ /// a copy.
+ unsigned HasRewrittenInit : 1;
+
/// The location where the default initializer expression was used.
SourceLocation Loc;
};
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c684d3b4a7810..d1bffc7e3e9c6 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2646,6 +2646,10 @@ def err_invalid_consteval_take_address : Error<
" of an immediate invocation">;
def err_invalid_consteval_call : Error<
"call to consteval function %q0 is not a constant expression">;
+def note_invalid_consteval_initializer : Note<
+ "in the default initalizer of %0">;
+def note_invalid_consteval_initializer_here : Note<
+ "initialized here %0">;
def err_invalid_consteval_decl_kind : Error<
"%0 cannot be declared consteval">;
def err_invalid_constexpr : Error<
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3d9d96c904a0a..1cbbcfd855121 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -1330,6 +1330,25 @@ class Sema final {
bool InDiscardedStatement;
bool InImmediateFunctionContext;
+ bool IsCurrentlyCheckingDefaultArgumentOrInitializer = false;
+
+ // When evaluating immediate functions in the initializer of a default
+ // argument or default member initializer, this is the declaration whose
+ // default initializer is being evaluated and the location of the call
+ // or constructor definition.
+ struct InitializationContext {
+ InitializationContext(SourceLocation Loc, ValueDecl *Decl,
+ DeclContext *Context)
+ : Loc(Loc), Decl(Decl), Context(Context) {
+ assert(Decl && Context && "invalid initialization context");
+ }
+
+ SourceLocation Loc;
+ ValueDecl *Decl = nullptr;
+ DeclContext *Context = nullptr;
+ };
+ llvm::Optional<InitializationContext> DelayedDefaultInitializationContext;
+
ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context,
unsigned NumCleanupObjects,
CleanupInfo ParentCleanup,
@@ -6211,19 +6230,22 @@ class Sema final {
bool IsStdInitListInitialization, bool RequiresZeroInit,
unsigned ConstructKind, SourceRange ParenRange);
+ ExprResult ConvertMemberDefaultInitExpression(FieldDecl *FD, Expr *InitExpr,
+ SourceLocation InitLoc);
+
ExprResult BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field);
/// Instantiate or parse a C++ default argument expression as necessary.
/// Return true on error.
bool CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
- ParmVarDecl *Param);
+ ParmVarDecl *Param, Expr *Init = nullptr,
+ bool SkipImmediateInvocations = true);
/// BuildCXXDefaultArgExpr - Creates a CXXDefaultArgExpr, instantiating
/// the default expr if needed.
- ExprResult BuildCXXDefaultArgExpr(SourceLocation CallLoc,
- FunctionDecl *FD,
- ParmVarDecl *Param);
+ ExprResult BuildCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
+ ParmVarDecl *Param, Expr *Init = nullptr);
/// FinalizeVarWithDestructor - Prepare for calling destructor on the
/// constructed variable.
@@ -9636,6 +9658,48 @@ class Sema final {
return ExprEvalContexts.back().isImmediateFunctionContext();
}
+ bool isCheckingDefaultArgumentOrInitializer() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ const ExpressionEvaluationContextRecord &Ctx = ExprEvalContexts.back();
+ return (Ctx.Context ==
+ ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed) ||
+ Ctx.IsCurrentlyCheckingDefaultArgumentOrInitializer;
+ }
+
+ llvm::Optional<ExpressionEvaluationContextRecord::InitializationContext>
+ InnermostDeclarationWithDelayedImmediateInvocations() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ for (const auto &Ctx : llvm::reverse(ExprEvalContexts)) {
+ if (Ctx.Context == ExpressionEvaluationContext::PotentiallyEvaluated &&
+ Ctx.DelayedDefaultInitializationContext)
+ return Ctx.DelayedDefaultInitializationContext;
+ if (Ctx.isConstantEvaluated() || Ctx.isImmediateFunctionContext() ||
+ Ctx.isUnevaluated())
+ break;
+ }
+ return std::nullopt;
+ }
+
+ llvm::Optional<ExpressionEvaluationContextRecord::InitializationContext>
+ OutermostDeclarationWithDelayedImmediateInvocations() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ llvm::Optional<ExpressionEvaluationContextRecord::InitializationContext>
+ Res;
+ for (auto &Ctx : llvm::reverse(ExprEvalContexts)) {
+ if (Ctx.Context == ExpressionEvaluationContext::PotentiallyEvaluated &&
+ !Ctx.DelayedDefaultInitializationContext && Res)
+ break;
+ if (Ctx.isConstantEvaluated() || Ctx.isImmediateFunctionContext() ||
+ Ctx.isUnevaluated())
+ break;
+ Res = Ctx.DelayedDefaultInitializationContext;
+ }
+ return Res;
+ }
+
/// RAII class used to determine whether SFINAE has
/// trapped any errors that occur during template argument
/// deduction.
diff --git a/clang/lib/AST/ASTImporter.cpp b/clang/lib/AST/ASTImporter.cpp
index e5704b39b60ee..e77f57008d82f 100644
--- a/clang/lib/AST/ASTImporter.cpp
+++ b/clang/lib/AST/ASTImporter.cpp
@@ -7687,9 +7687,16 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
if (Error Err = ImportDefaultArgOfParmVarDecl(*FromParam, ToParam))
return std::move(Err);
}
-
+ Expr *RewrittenInit = nullptr;
+ if (E->hasRewrittenInit()) {
+ ExpectedExpr ExprOrErr = import(E->getRewrittenExpr());
+ if (!ExprOrErr)
+ return ExprOrErr.takeError();
+ RewrittenInit = ExprOrErr.get();
+ }
return CXXDefaultArgExpr::Create(Importer.getToContext(), *ToUsedLocOrErr,
- *ToParamOrErr, *UsedContextOrErr);
+ *ToParamOrErr, RewrittenInit,
+ *UsedContextOrErr);
}
ExpectedStmt
@@ -8381,8 +8388,16 @@ ExpectedStmt ASTNodeImporter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
ToField->setInClassInitializer(*ToInClassInitializerOrErr);
}
+ Expr *RewrittenInit = nullptr;
+ if (E->hasRewrittenInit()) {
+ ExpectedExpr ExprOrErr = import(E->getRewrittenExpr());
+ if (!ExprOrErr)
+ return ExprOrErr.takeError();
+ RewrittenInit = ExprOrErr.get();
+ }
+
return CXXDefaultInitExpr::Create(Importer.getToContext(), *ToBeginLocOrErr,
- ToField, *UsedContextOrErr);
+ ToField, *UsedContextOrErr, RewrittenInit);
}
ExpectedStmt ASTNodeImporter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) {
diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 2acb3915aa586..47c1f4891a139 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -2897,8 +2897,7 @@ Expr *ParmVarDecl::getDefaultArg() {
Expr *Arg = getInit();
if (auto *E = dyn_cast_or_null<FullExpr>(Arg))
- if (!isa<ConstantExpr>(E))
- return E->getSubExpr();
+ return E->getSubExpr();
return Arg;
}
diff --git a/clang/lib/AST/ExprCXX.cpp b/clang/lib/AST/ExprCXX.cpp
index b988f0fe13f7f..f7674b4280ad5 100644
--- a/clang/lib/AST/ExprCXX.cpp
+++ b/clang/lib/AST/ExprCXX.cpp
@@ -949,9 +949,43 @@ const IdentifierInfo *UserDefinedLiteral::getUDSuffix() const {
return cast<FunctionDecl>(getCalleeDecl())->getLiteralIdentifier();
}
+CXXDefaultArgExpr *CXXDefaultArgExpr::CreateEmpty(const ASTContext &C,
+ bool HasRewrittenInit) {
+ size_t Size = totalSizeToAlloc<Expr *>(HasRewrittenInit);
+ auto *Mem = C.Allocate(Size, alignof(CXXDefaultArgExpr));
+ return new (Mem) CXXDefaultArgExpr(EmptyShell(), HasRewrittenInit);
+}
+
+CXXDefaultArgExpr *CXXDefaultArgExpr::Create(const ASTContext &C,
+ SourceLocation Loc,
+ ParmVarDecl *Param,
+ Expr *RewrittenExpr,
+ DeclContext *UsedContext) {
+ size_t Size = totalSizeToAlloc<Expr *>(RewrittenExpr != nullptr);
+ auto *Mem = C.Allocate(Size, alignof(CXXDefaultArgExpr));
+ return new (Mem) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param,
+ RewrittenExpr, UsedContext);
+}
+
+Expr *CXXDefaultArgExpr::getExpr() {
+ return CXXDefaultArgExprBits.HasRewrittenInit ? getAdjustedRewrittenExpr()
+ : getParam()->getDefaultArg();
+}
+
+Expr *CXXDefaultArgExpr::getAdjustedRewrittenExpr() {
+ assert(hasRewrittenInit() &&
+ "expected this CXXDefaultArgExpr to have a rewritten init.");
+ Expr *Init = getRewrittenExpr();
+ if (auto *E = dyn_cast_if_present<FullExpr>(Init))
+ if (!isa<ConstantExpr>(E))
+ return E->getSubExpr();
+ return Init;
+}
+
CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &Ctx,
SourceLocation Loc, FieldDecl *Field,
- QualType Ty, DeclContext *UsedContext)
+ QualType Ty, DeclContext *UsedContext,
+ Expr *RewrittenInitExpr)
: Expr(CXXDefaultInitExprClass, Ty.getNonLValueExprType(Ctx),
Ty->isLValueReferenceType() ? VK_LValue
: Ty->isRValueReferenceType() ? VK_XValue
@@ -959,11 +993,43 @@ CXXDefaultInitExpr::CXXDefaultInitExpr(const ASTContext &Ctx,
/*FIXME*/ OK_Ordinary),
Field(Field), UsedContext(UsedContext) {
CXXDefaultInitExprBits.Loc = Loc;
+ CXXDefaultInitExprBits.HasRewrittenInit = RewrittenInitExpr != nullptr;
+
+ if (CXXDefaultInitExprBits.HasRewrittenInit)
+ *getTrailingObjects<Expr *>() = RewrittenInitExpr;
+
assert(Field->hasInClassInitializer());
setDependence(computeDependence(this));
}
+CXXDefaultInitExpr *CXXDefaultInitExpr::CreateEmpty(const ASTContext &C,
+ bool HasRewrittenInit) {
+ size_t Size = totalSizeToAlloc<Expr *>(HasRewrittenInit);
+ auto *Mem = C.Allocate(Size, alignof(CXXDefaultInitExpr));
+ return new (Mem) CXXDefaultInitExpr(EmptyShell(), HasRewrittenInit);
+}
+
+CXXDefaultInitExpr *CXXDefaultInitExpr::Create(const ASTContext &Ctx,
+ SourceLocation Loc,
+ FieldDecl *Field,
+ DeclContext *UsedContext,
+ Expr *RewrittenInitExpr) {
+
+ size_t Size = totalSizeToAlloc<Expr *>(RewrittenInitExpr != nullptr);
+ auto *Mem = Ctx.Allocate(Size, alignof(CXXDefaultInitExpr));
+ return new (Mem) CXXDefaultInitExpr(Ctx, Loc, Field, Field->getType(),
+ UsedContext, RewrittenInitExpr);
+}
+
+Expr *CXXDefaultInitExpr::getExpr() {
+ assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
+ if (hasRewrittenInit())
+ return getRewrittenExpr();
+
+ return Field->getInClassInitializer();
+}
+
CXXTemporary *CXXTemporary::Create(const ASTContext &C,
const CXXDestructorDecl *Destructor) {
return new (C) CXXTemporary(Destructor);
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index d918ea26b9d9d..3a7f5426d4a70 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -648,6 +648,11 @@ void Parser::ParseLexedMemberInitializer(LateParsedMemberInitializer &MI) {
Actions.ActOnStartCXXInClassMemberInitializer();
+ // The initializer isn't actually potentially evaluated unless it is
+ // used.
+ EnterExpressionEvaluationContext Eval(
+ Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed);
+
ExprResult Init = ParseCXXMemberInitializer(MI.Field, /*IsFunction=*/false,
EqualLoc);
diff --git a/clang/lib/Parse/ParseDeclCXX.cpp b/clang/lib/Parse/ParseDeclCXX.cpp
index b3ab5bcb238ab..ebc86c00870e7 100644
--- a/clang/lib/Parse/ParseDeclCXX.cpp
+++ b/clang/lib/Parse/ParseDeclCXX.cpp
@@ -3191,7 +3191,11 @@ ExprResult Parser::ParseCXXMemberInitializer(Decl *D, bool IsFunction,
"Data member initializer not starting with '=' or '{'");
EnterExpressionEvaluationContext Context(
- Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, D);
+ Actions,
+ isa_and_present<FieldDecl>(D)
+ ? Sema::ExpressionEvaluationContext::PotentiallyEvaluatedIfUsed
+ : Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
+ D);
if (TryConsumeToken(tok::equal, EqualLoc)) {
if (Tok.is(tok::kw_delete)) {
// In principle, an initializer of '= delete p;' is legal, but it will
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 9a80a4874b6db..95797215235b4 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -4059,6 +4059,21 @@ ExprResult Sema::ActOnRequiresClause(ExprResult ConstraintExpr) {
return ConstraintExpr;
}
+ExprResult Sema::ConvertMemberDefaultInitExpression(FieldDecl *FD,
+ Expr *InitExpr,
+ SourceLocation InitLoc) {
+ InitializedEntity Entity =
+ InitializedEntity::InitializeMemberFromDefaultMemberInitializer(FD);
+ InitializationKind Kind =
+ FD->getInClassInitStyle() == ICIS_ListInit
+ ? InitializationKind::CreateDirectList(InitExpr->getBeginLoc(),
+ InitExpr->getBeginLoc(),
+ InitExpr->getEndLoc())
+ : InitializationKind::CreateCopy(InitExpr->getBeginLoc(), InitLoc);
+ InitializationSequence Seq(*this, Entity, Kind, InitExpr);
+ return Seq.Perform(*this, Entity, Kind, InitExpr);
+}
+
/// This is invoked after parsing an in-class initializer for a
/// non-static C++ class member, and after instantiating an in-class initializer
/// in a class template. Such actions are deferred until the class is complete.
@@ -4087,31 +4102,18 @@ void Sema::ActOnFinishCXXInClassMemberInitializer(Decl *D,
ExprResult Init = InitExpr;
if (!FD->getType()->isDependentType() && !InitExpr->isTypeDependent()) {
- InitializedEntity Entity =
- InitializedEntity::InitializeMemberFromDefaultMemberInitializer(FD);
- InitializationKind Kind =
- FD->getInClassInitStyle() == ICIS_ListInit
- ? InitializationKind::CreateDirectList(InitExpr->getBeginLoc(),
- InitExpr->getBeginLoc(),
- InitExpr->getEndLoc())
- : InitializationKind::CreateCopy(InitExpr->getBeginLoc(), InitLoc);
- InitializationSequence Seq(*this, Entity, Kind, InitExpr);
- Init = Seq.Perform(*this, Entity, Kind, InitExpr);
+ Init = ConvertMemberDefaultInitExpression(FD, InitExpr, InitLoc);
+ // C++11 [class.base.init]p7:
+ // The initialization of each base and member constitutes a
+ // full-expression.
+ if (!Init.isInvalid())
+ Init = ActOnFinishFullExpr(Init.get(), /*DiscarededValue=*/false);
if (Init.isInvalid()) {
FD->setInvalidDecl();
return;
}
}
- // C++11 [class.base.init]p7:
- // The initialization of each base and member constitutes a
- // full-expression.
- Init = ActOnFinishFullExpr(Init.get(), InitLoc, /*DiscardedValue*/ false);
- if (Init.isInvalid()) {
- FD->setInvalidDecl();
- return;
- }
-
InitExpr = Init.get();
FD->setInClassInitializer(InitExpr);
@@ -15651,70 +15653,6 @@ Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
Constructor);
}
-ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
- assert(Field->hasInClassInitializer());
-
- // If we already have the in-class initializer nothing needs to be done.
- if (Field->getInClassInitializer())
- return CXXDefaultInitExpr::Create(Context, Loc, Field, CurContext);
-
- // If we might have already tried and failed to instantiate, don't try again.
- if (Field->isInvalidDecl())
- return ExprError();
-
- // Maybe we haven't instantiated the in-class initializer. Go check the
- // pattern FieldDecl to see if it has one.
- CXXRecordDecl *ParentRD = cast<CXXRecordDecl>(Field->getParent());
-
- if (isTemplateInstantiation(ParentRD->getTemplateSpecializationKind())) {
- CXXRecordDecl *ClassPattern = ParentRD->getTemplateInstantiationPattern();
- DeclContext::lookup_result Lookup =
- ClassPattern->lookup(Field->getDeclName());
-
- FieldDecl *Pattern = nullptr;
- for (auto *L : Lookup) {
- if (isa<FieldDecl>(L)) {
- Pattern = cast<FieldDecl>(L);
- break;
- }
- }
- assert(Pattern && "We must have set the Pattern!");
-
- if (!Pattern->hasInClassInitializer() ||
- InstantiateInClassInitializer(Loc, Field, Pattern,
- getTemplateInstantiationArgs(Field))) {
- // Don't diagnose this again.
- Field->setInvalidDecl();
- return ExprError();
- }
- return CXXDefaultInitExpr::Create(Context, Loc, Field, CurContext);
- }
-
- // DR1351:
- // If the brace-or-equal-initializer of a non-static data member
- // invokes a defaulted default constructor of its class or of an
- // enclosing class in a potentially evaluated subexpression, the
- // program is ill-formed.
- //
- // This resolution is unworkable: the exception specification of the
- // default constructor can be needed in an unevaluated context, in
- // particular, in the operand of a noexcept-expression, and we can be
- // unable to compute an exception specification for an enclosed class.
- //
- // Any attempt to resolve the exception specification of a defaulted default
- // constructor before the initializer is lexically complete will ultimately
- // come here at which point we can diagnose it.
- RecordDecl *OutermostClass = ParentRD->getOuterLexicalRecordContext();
- Diag(Loc, diag::err_default_member_initializer_not_yet_parsed)
- << OutermostClass << Field;
- Diag(Field->getEndLoc(),
- diag::note_default_member_initializer_not_yet_parsed);
- // Recover by marking the field invalid, unless we're in a SFINAE context.
- if (!isSFINAEContext())
- Field->setInvalidDecl();
- return ExprError();
-}
-
void Sema::FinalizeVarWithDestructor(VarDecl *VD, const RecordType *Record) {
if (VD->isInvalidDecl()) return;
// If initializing the variable failed, don't also diagnose problems with
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b14bd97eccb8a..1fd973bb31fb6 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -5860,8 +5860,10 @@ Sema::CreateBuiltinArraySubscriptExpr(Expr *Base, SourceLocation LLoc,
}
bool Sema::CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
- ParmVarDecl *Param) {
+ ParmVarDecl *Param, Expr *RewrittenInit,
+ bool SkipImmediateInvocations) {
if (Param->hasUnparsedDefaultArg()) {
+ assert(!RewrittenInit && "Should not have a rewritten init expression yet");
// If we've already cleared out the location for the default argument,
// that means we're parsing it right now.
if (!UnparsedDefaultArgLocs.count(Param)) {
@@ -5878,11 +5880,14 @@ bool Sema::CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
return true;
}
- if (Param->hasUninstantiatedDefaultArg() &&
- InstantiateDefaultArgument(CallLoc, FD, Param))
- return true;
+ if (Param->hasUninstantiatedDefaultArg()) {
+ assert(!RewrittenInit && "Should not have a rewitten init expression yet");
+ if (InstantiateDefaultArgument(CallLoc, FD, Param))
+ return true;
+ }
- assert(Param->hasInit() && "default argument but no initializer?");
+ Expr *Init = RewrittenInit ? RewrittenInit : Param->getInit();
+ assert(Init && "default argument but no initializer?");
// If the default expression creates temporaries, we need to
// push them to the current stack of expression temporaries so they'll
@@ -5891,34 +5896,258 @@ bool Sema::CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
// bound temporaries; see the comment in PR5810.
// We don't need to do that with block decls, though, because
// blocks in default argument expression can never capture anything.
- if (auto Init = dyn_cast<ExprWithCleanups>(Param->getInit())) {
+ if (auto *InitWithCleanup = dyn_cast<ExprWithCleanups>(Init)) {
// Set the "needs cleanups" bit regardless of whether there are
// any explicit objects.
- Cleanup.setExprNeedsCleanups(Init->cleanupsHaveSideEffects());
-
+ Cleanup.setExprNeedsCleanups(InitWithCleanup->cleanupsHaveSideEffects());
// Append all the objects to the cleanup list. Right now, this
// should always be a no-op, because blocks in default argument
// expressions should never be able to capture anything.
- assert(!Init->getNumObjects() &&
+ assert(!InitWithCleanup->getNumObjects() &&
"default argument expression has capturing blocks?");
}
-
- // We already type-checked the argument, so we know it works.
- // Just mark all of the declarations in this potentially-evaluated expression
- // as being "referenced".
EnterExpressionEvaluationContext EvalContext(
*this, ExpressionEvaluationContext::PotentiallyEvaluated, Param);
- MarkDeclarationsReferencedInExpr(Param->getDefaultArg(),
- /*SkipLocalVariables=*/true);
+ ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
+ SkipImmediateInvocations;
+ MarkDeclarationsReferencedInExpr(Init, /*SkipLocalVariables*/ true);
return false;
}
+struct ImmediateCallVisitor : public RecursiveASTVisitor<ImmediateCallVisitor> {
+ bool HasImmediateCalls = false;
+
+ bool shouldVisitImplicitCode() const { return true; }
+
+ bool VisitCallExpr(CallExpr *E) {
+ if (const FunctionDecl *FD = E->getDirectCallee())
+ HasImmediateCalls |= FD->isConsteval();
+ return RecursiveASTVisitor<ImmediateCallVisitor>::VisitStmt(E);
+ }
+
+ // SourceLocExpr are not immediate invocations
+ // but CXXDefaultInitExpr/CXXDefaultArgExpr containing a SourceLocExpr
+ // need to be rebuilt so that they refer to the correct SourceLocation and
+ // DeclContext.
+ bool VisitSourceLocExpr(SourceLocExpr *E) {
+ HasImmediateCalls = true;
+ return RecursiveASTVisitor<ImmediateCallVisitor>::VisitStmt(E);
+ }
+
+ // A nested lambda might have parameters with immediate invocations
+ // in their default arguments.
+ // The compound statement is not visited (as it does not constitute a
+ // subexpression).
+ // FIXME: We should consider visiting and transforming captures
+ // with init expressions.
+ bool VisitLambdaExpr(LambdaExpr *E) {
+ return VisitCXXMethodDecl(E->getCallOperator());
+ }
+
+ // Blocks don't support default parameters, and, as for lambdas,
+ // we don't consider their body a subexpression.
+ bool VisitBlockDecl(BlockDecl *B) { return false; }
+
+ bool VisitCompoundStmt(CompoundStmt *B) { return false; }
+
+ bool VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
+ return TraverseStmt(E->getExpr());
+ }
+
+ bool VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
+ return TraverseStmt(E->getExpr());
+ }
+};
+
+struct EnsureImmediateInvocationInDefaultArgs
+ : TreeTransform<EnsureImmediateInvocationInDefaultArgs> {
+ EnsureImmediateInvocationInDefaultArgs(Sema &SemaRef)
+ : TreeTransform(SemaRef) {}
+
+ // Lambda can only have immediate invocations in the default
+ // args of their parameters, which is transformed upon calling the closure.
+ // The body is not a subexpression, so we have nothing to do.
+ // FIXME: Immediate calls in capture initializers should be transformed.
+ ExprResult TransformLambdaExpr(LambdaExpr *E) { return E; }
+ ExprResult TransformBlockExpr(BlockExpr *E) { return E; }
+
+ // Make sure we don't rebuild the this pointer as it would
+ // cause it to incorrectly point it to the outermost class
+ // in the case of nested struct initialization.
+ ExprResult TransformCXXThisExpr(CXXThisExpr *E) { return E; }
+};
+
ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc,
- FunctionDecl *FD, ParmVarDecl *Param) {
+ FunctionDecl *FD, ParmVarDecl *Param,
+ Expr *Init) {
assert(Param->hasDefaultArg() && "can't build nonexistent default arg");
- if (CheckCXXDefaultArgExpr(CallLoc, FD, Param))
+
+ bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
+
+ llvm::Optional<ExpressionEvaluationContextRecord::InitializationContext>
+ InitializationContext =
+ OutermostDeclarationWithDelayedImmediateInvocations();
+ if (!InitializationContext.has_value())
+ InitializationContext.emplace(CallLoc, Param, CurContext);
+
+ if (!Init && !Param->hasUnparsedDefaultArg()) {
+ // Mark that we are replacing a default argument first.
+ // If we are instantiating a template we won't have to
+ // retransform immediate calls.
+ EnterExpressionEvaluationContext EvalContext(
+ *this, ExpressionEvaluationContext::PotentiallyEvaluated, Param);
+
+ if (Param->hasUninstantiatedDefaultArg()) {
+ if (InstantiateDefaultArgument(CallLoc, FD, Param))
+ return ExprError();
+ }
+ // CWG2631
+ // An immediate invocation that is not evaluated where it appears is
+ // evaluated and checked for whether it is a constant expression at the
+ // point where the enclosing initializer is used in a function call.
+ ImmediateCallVisitor V;
+ if (!NestedDefaultChecking)
+ V.TraverseDecl(Param);
+ if (V.HasImmediateCalls) {
+ ExprEvalContexts.back().DelayedDefaultInitializationContext = {
+ CallLoc, Param, CurContext};
+ EnsureImmediateInvocationInDefaultArgs Immediate(*this);
+ ExprResult Res = Immediate.TransformInitializer(Param->getInit(),
+ /*NotCopy=*/false);
+ if (Res.isInvalid())
+ return ExprError();
+ Res = ConvertParamDefaultArgument(Param, Res.get(),
+ Res.get()->getBeginLoc());
+ if (Res.isInvalid())
+ return ExprError();
+ Init = Res.get();
+ }
+ }
+
+ if (CheckCXXDefaultArgExpr(
+ CallLoc, FD, Param, Init,
+ /*SkipImmediateInvocations=*/NestedDefaultChecking))
+ return ExprError();
+
+ return CXXDefaultArgExpr::Create(Context, InitializationContext->Loc, Param,
+ Init, InitializationContext->Context);
+}
+
+ExprResult Sema::BuildCXXDefaultInitExpr(SourceLocation Loc, FieldDecl *Field) {
+ assert(Field->hasInClassInitializer());
+
+ // If we might have already tried and failed to instantiate, don't try again.
+ if (Field->isInvalidDecl())
return ExprError();
- return CXXDefaultArgExpr::Create(Context, CallLoc, Param, CurContext);
+
+ auto *ParentRD = cast<CXXRecordDecl>(Field->getParent());
+
+ llvm::Optional<ExpressionEvaluationContextRecord::InitializationContext>
+ InitializationContext =
+ OutermostDeclarationWithDelayedImmediateInvocations();
+ if (!InitializationContext.has_value())
+ InitializationContext.emplace(Loc, Field, CurContext);
+
+ Expr *Init = nullptr;
+
+ bool NestedDefaultChecking = isCheckingDefaultArgumentOrInitializer();
+
+ EnterExpressionEvaluationContext EvalContext(
+ *this, ExpressionEvaluationContext::PotentiallyEvaluated, Field);
+
+ if (!Field->getInClassInitializer()) {
+ // Maybe we haven't instantiated the in-class initializer. Go check the
+ // pattern FieldDecl to see if it has one.
+ if (isTemplateInstantiation(ParentRD->getTemplateSpecializationKind())) {
+ CXXRecordDecl *ClassPattern = ParentRD->getTemplateInstantiationPattern();
+ DeclContext::lookup_result Lookup =
+ ClassPattern->lookup(Field->getDeclName());
+
+ FieldDecl *Pattern = nullptr;
+ for (auto *L : Lookup) {
+ if ((Pattern = dyn_cast<FieldDecl>(L)))
+ break;
+ }
+ assert(Pattern && "We must have set the Pattern!");
+ if (!Pattern->hasInClassInitializer() ||
+ InstantiateInClassInitializer(Loc, Field, Pattern,
+ getTemplateInstantiationArgs(Field))) {
+ Field->setInvalidDecl();
+ return ExprError();
+ }
+ }
+ }
+
+ // CWG2631
+ // An immediate invocation that is not evaluated where it appears is
+ // evaluated and checked for whether it is a constant expression at the
+ // point where the enclosing initializer is used in a [...] a constructor
+ // definition, or an aggregate initialization.
+ ImmediateCallVisitor V;
+ if (!NestedDefaultChecking)
+ V.TraverseDecl(Field);
+ if (V.HasImmediateCalls) {
+ ExprEvalContexts.back().DelayedDefaultInitializationContext = {Loc, Field,
+ CurContext};
+ ExprEvalContexts.back().IsCurrentlyCheckingDefaultArgumentOrInitializer =
+ NestedDefaultChecking;
+
+ EnsureImmediateInvocationInDefaultArgs Immediate(*this);
+
+ ExprResult Res =
+ Immediate.TransformInitializer(Field->getInClassInitializer(),
+ /*CXXDirectInit=*/false);
+ if (!Res.isInvalid())
+ Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
+ if (Res.isInvalid()) {
+ Field->setInvalidDecl();
+ return ExprError();
+ }
+ Init = Res.get();
+ }
+
+ if (Field->getInClassInitializer()) {
+ Expr *E = Init ? Init : Field->getInClassInitializer();
+ if (!NestedDefaultChecking)
+ MarkDeclarationsReferencedInExpr(E, /*SkipLocalVariables=*/false);
+ // C++11 [class.base.init]p7:
+ // The initialization of each base and member constitutes a
+ // full-expression.
+ ExprResult Res = ActOnFinishFullExpr(E, /*DiscardedValue=*/false);
+ if (Res.isInvalid()) {
+ Field->setInvalidDecl();
+ return ExprError();
+ }
+ Init = Res.get();
+
+ return CXXDefaultInitExpr::Create(Context, InitializationContext->Loc,
+ Field, InitializationContext->Context,
+ Init);
+ }
+
+ // DR1351:
+ // If the brace-or-equal-initializer of a non-static data member
+ // invokes a defaulted default constructor of its class or of an
+ // enclosing class in a potentially evaluated subexpression, the
+ // program is ill-formed.
+ //
+ // This resolution is unworkable: the exception specification of the
+ // default constructor can be needed in an unevaluated context, in
+ // particular, in the operand of a noexcept-expression, and we can be
+ // unable to compute an exception specification for an enclosed class.
+ //
+ // Any attempt to resolve the exception specification of a defaulted default
+ // constructor before the initializer is lexically complete will ultimately
+ // come here at which point we can diagnose it.
+ RecordDecl *OutermostClass = ParentRD->getOuterLexicalRecordContext();
+ Diag(Loc, diag::err_default_member_initializer_not_yet_parsed)
+ << OutermostClass << Field;
+ Diag(Field->getEndLoc(),
+ diag::note_default_member_initializer_not_yet_parsed);
+ // Recover by marking the field invalid, unless we're in a SFINAE context.
+ if (!isSFINAEContext())
+ Field->setInvalidDecl();
+ return ExprError();
}
Sema::VariadicCallType
@@ -17547,6 +17776,7 @@ void Sema::CheckUnusedVolatileAssignment(Expr *E) {
ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) {
if (isUnevaluatedContext() || !E.isUsable() || !Decl ||
!Decl->isConsteval() || isConstantEvaluated() ||
+ isCheckingDefaultArgumentOrInitializer() ||
RebuildingImmediateInvocation || isImmediateFunctionContext())
return E;
@@ -17592,8 +17822,14 @@ static void EvaluateAndDiagnoseImmediateInvocation(
FD = Call->getConstructor();
else
llvm_unreachable("unhandled decl kind");
- assert(FD->isConsteval());
+ assert(FD && FD->isConsteval());
SemaRef.Diag(CE->getBeginLoc(), diag::err_invalid_consteval_call) << FD;
+ if (auto Context =
+ SemaRef.InnermostDeclarationWithDelayedImmediateInvocations()) {
+ SemaRef.Diag(Context->Loc, diag::note_invalid_consteval_initializer)
+ << Context->Decl;
+ SemaRef.Diag(Context->Decl->getBeginLoc(), diag::note_declared_at);
+ }
for (auto &Note : Notes)
SemaRef.Diag(Note.first, Note.second);
return;
@@ -18178,6 +18414,16 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func,
});
}
+ // If a constructor was defined in the context of a default parameter
+ // or of another default member initializer (ie a PotentiallyEvaluatedIfUsed
+ // context), its initializers may not be referenced yet.
+ if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(Func)) {
+ for (CXXCtorInitializer *Init : Constructor->inits()) {
+ if (Init->isInClassMemberInitializer())
+ MarkDeclarationsReferencedInExpr(Init->getInit());
+ }
+ }
+
// C++14 [except.spec]p17:
// An exception-specification is considered to be needed when:
// - the function is odr-used or, if it appears in an unevaluated operand,
@@ -19606,7 +19852,10 @@ static void DoMarkVarDeclReferenced(
switch (OdrUse) {
case OdrUseContext::None:
- assert((!E || isa<FunctionParmPackExpr>(E)) &&
+ // In some cases, a variable may not have been marked unevaluated, if it
+ // appears in a defaukt initializer.
+ assert((!E || isa<FunctionParmPackExpr>(E) ||
+ SemaRef.isUnevaluatedContext()) &&
"missing non-odr-use marking for unevaluated decl ref");
break;
@@ -19739,7 +19988,8 @@ void Sema::MarkDeclRefReferenced(DeclRefExpr *E, const Expr *Base) {
if (auto *FD = dyn_cast<FunctionDecl>(E->getDecl()))
if (!isUnevaluatedContext() && !isConstantEvaluated() &&
- !isImmediateFunctionContext() && FD->isConsteval() &&
+ !isImmediateFunctionContext() &&
+ !isCheckingDefaultArgumentOrInitializer() && FD->isConsteval() &&
!RebuildingImmediateInvocation && !FD->isDependentContext())
ExprEvalContexts.back().ReferenceToConsteval.insert(E);
MarkExprReferenced(*this, E->getLocation(), E->getDecl(), E, OdrUse,
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index c4531470119bd..effb63326b38a 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1982,9 +1982,9 @@ ExprResult TemplateInstantiator::TransformCXXDefaultArgExpr(
assert(!cast<FunctionDecl>(E->getParam()->getDeclContext())->
getDescribedFunctionTemplate() &&
"Default arg expressions are never formed in dependent cases.");
- return SemaRef.BuildCXXDefaultArgExpr(E->getUsedLocation(),
- cast<FunctionDecl>(E->getParam()->getDeclContext()),
- E->getParam());
+ return SemaRef.BuildCXXDefaultArgExpr(
+ E->getUsedLocation(), cast<FunctionDecl>(E->getParam()->getDeclContext()),
+ E->getParam());
}
template<typename Fn>
@@ -3405,6 +3405,8 @@ bool Sema::InstantiateInClassInitializer(
ContextRAII SavedContext(*this, Instantiation->getParent());
EnterExpressionEvaluationContext EvalContext(
*this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
+ ExprEvalContexts.back().DelayedDefaultInitializationContext = {
+ PointOfInstantiation, Instantiation, CurContext};
LocalInstantiationScope Scope(*this, true);
diff --git a/clang/lib/Sema/TreeTransform.h b/clang/lib/Sema/TreeTransform.h
index 075066472df0c..debe783e64409 100644
--- a/clang/lib/Sema/TreeTransform.h
+++ b/clang/lib/Sema/TreeTransform.h
@@ -3210,9 +3210,10 @@ class TreeTransform {
/// By default, builds a new default-argument expression, which does not
/// require any semantic analysis. Subclasses may override this routine to
/// provide
diff erent behavior.
- ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc, ParmVarDecl *Param) {
+ ExprResult RebuildCXXDefaultArgExpr(SourceLocation Loc, ParmVarDecl *Param,
+ Expr *RewrittenExpr) {
return CXXDefaultArgExpr::Create(getSema().Context, Loc, Param,
- getSema().CurContext);
+ RewrittenExpr, getSema().CurContext);
}
/// Build a new C++11 default-initialization expression.
@@ -3222,8 +3223,7 @@ class TreeTransform {
/// routine to provide
diff erent behavior.
ExprResult RebuildCXXDefaultInitExpr(SourceLocation Loc,
FieldDecl *Field) {
- return CXXDefaultInitExpr::Create(getSema().Context, Loc, Field,
- getSema().CurContext);
+ return getSema().BuildCXXDefaultInitExpr(Loc, Field);
}
/// Build a new C++ zero-initialization expression.
@@ -12163,11 +12163,20 @@ TreeTransform<Derived>::TransformCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
if (!Param)
return ExprError();
+ ExprResult InitRes;
+ if (E->hasRewrittenInit()) {
+ InitRes = getDerived().TransformExpr(E->getRewrittenExpr());
+ if (InitRes.isInvalid())
+ return ExprError();
+ }
+
if (!getDerived().AlwaysRebuild() && Param == E->getParam() &&
- E->getUsedContext() == SemaRef.CurContext)
+ E->getUsedContext() == SemaRef.CurContext &&
+ InitRes.get() == E->getRewrittenExpr())
return E;
- return getDerived().RebuildCXXDefaultArgExpr(E->getUsedLocation(), Param);
+ return getDerived().RebuildCXXDefaultArgExpr(E->getUsedLocation(), Param,
+ InitRes.get());
}
template<typename Derived>
diff --git a/clang/lib/Sema/UsedDeclVisitor.h b/clang/lib/Sema/UsedDeclVisitor.h
index 24b7342b3fb40..580d702f96fe5 100644
--- a/clang/lib/Sema/UsedDeclVisitor.h
+++ b/clang/lib/Sema/UsedDeclVisitor.h
@@ -82,11 +82,28 @@ class UsedDeclVisitor : public EvaluatedExprVisitor<Derived> {
void VisitCXXConstructExpr(CXXConstructExpr *E) {
asImpl().visitUsedDecl(E->getBeginLoc(), E->getConstructor());
+ CXXConstructorDecl *D = E->getConstructor();
+ for (const CXXCtorInitializer *Init : D->inits()) {
+ if (Init->isInClassMemberInitializer())
+ asImpl().Visit(Init->getInit());
+ }
Inherited::VisitCXXConstructExpr(E);
}
void VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
asImpl().Visit(E->getExpr());
+ Inherited::VisitCXXDefaultArgExpr(E);
+ }
+
+ void VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
+ asImpl().Visit(E->getExpr());
+ Inherited::VisitCXXDefaultInitExpr(E);
+ }
+
+ void VisitInitListExpr(InitListExpr *ILE) {
+ if (ILE->hasArrayFiller())
+ asImpl().Visit(ILE->getArrayFiller());
+ Inherited::VisitInitListExpr(ILE);
}
void visitUsedDecl(SourceLocation Loc, Decl *D) {
diff --git a/clang/lib/Serialization/ASTReaderStmt.cpp b/clang/lib/Serialization/ASTReaderStmt.cpp
index 2995becb7dd8d..9adcae6d52e79 100644
--- a/clang/lib/Serialization/ASTReaderStmt.cpp
+++ b/clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1832,13 +1832,19 @@ void ASTStmtReader::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
E->Param = readDeclAs<ParmVarDecl>();
E->UsedContext = readDeclAs<DeclContext>();
E->CXXDefaultArgExprBits.Loc = readSourceLocation();
+ E->CXXDefaultArgExprBits.HasRewrittenInit = Record.readInt();
+ if (E->CXXDefaultArgExprBits.HasRewrittenInit)
+ *E->getTrailingObjects<Expr *>() = Record.readSubExpr();
}
void ASTStmtReader::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
VisitExpr(E);
+ E->CXXDefaultInitExprBits.HasRewrittenInit = Record.readInt();
E->Field = readDeclAs<FieldDecl>();
E->UsedContext = readDeclAs<DeclContext>();
E->CXXDefaultInitExprBits.Loc = readSourceLocation();
+ if (E->CXXDefaultInitExprBits.HasRewrittenInit)
+ *E->getTrailingObjects<Expr *>() = Record.readSubExpr();
}
void ASTStmtReader::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E) {
@@ -3837,11 +3843,13 @@ Stmt *ASTReader::ReadStmtFromStream(ModuleFile &F) {
break;
case EXPR_CXX_DEFAULT_ARG:
- S = new (Context) CXXDefaultArgExpr(Empty);
+ S = CXXDefaultArgExpr::CreateEmpty(
+ Context, /*HasRewrittenInit=*/Record[ASTStmtReader::NumExprFields]);
break;
case EXPR_CXX_DEFAULT_INIT:
- S = new (Context) CXXDefaultInitExpr(Empty);
+ S = CXXDefaultInitExpr::CreateEmpty(
+ Context, /*HasRewrittenInit=*/Record[ASTStmtReader::NumExprFields]);
break;
case EXPR_CXX_BIND_TEMPORARY:
diff --git a/clang/lib/Serialization/ASTWriterStmt.cpp b/clang/lib/Serialization/ASTWriterStmt.cpp
index 0dc9d4c7c859b..d5869a699ef2c 100644
--- a/clang/lib/Serialization/ASTWriterStmt.cpp
+++ b/clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1745,14 +1745,20 @@ void ASTStmtWriter::VisitCXXDefaultArgExpr(CXXDefaultArgExpr *E) {
Record.AddDeclRef(E->getParam());
Record.AddDeclRef(cast_or_null<Decl>(E->getUsedContext()));
Record.AddSourceLocation(E->getUsedLocation());
+ Record.push_back(E->hasRewrittenInit());
+ if (E->hasRewrittenInit())
+ Record.AddStmt(E->getRewrittenExpr());
Code = serialization::EXPR_CXX_DEFAULT_ARG;
}
void ASTStmtWriter::VisitCXXDefaultInitExpr(CXXDefaultInitExpr *E) {
VisitExpr(E);
+ Record.push_back(E->hasRewrittenInit());
Record.AddDeclRef(E->getField());
Record.AddDeclRef(cast_or_null<Decl>(E->getUsedContext()));
Record.AddSourceLocation(E->getExprLoc());
+ if (E->hasRewrittenInit())
+ Record.AddStmt(E->getRewrittenExpr());
Code = serialization::EXPR_CXX_DEFAULT_INIT;
}
diff --git a/clang/test/AST/ast-dump-records.cpp b/clang/test/AST/ast-dump-records.cpp
index 3dc087709a836..7a93b69d37ba6 100644
--- a/clang/test/AST/ast-dump-records.cpp
+++ b/clang/test/AST/ast-dump-records.cpp
@@ -288,3 +288,29 @@ struct Derived6 : virtual public Bases... {
// CHECK: CXXRecordDecl 0x{{[^ ]*}} <line:[[@LINE-1]]:1, line:[[@LINE+2]]:1> line:[[@LINE-1]]:8 struct Derived6 definition
// CHECK: virtual public 'Bases'...
};
+
+class NonTrivial {
+// CHECK: |-CXXRecordDecl {{.*}} referenced class NonTrivial definition
+ public:
+ NonTrivial();
+// CHECK: | |-CXXConstructorDecl {{.*}} referenced NonTrivial 'void ()'
+ ~NonTrivial();
+// CHECK: | |-CXXDestructorDecl {{.*}} referenced ~NonTrivial 'void () noexcept'
+};
+
+struct CheckFullExpression {
+// CHECK: |-CXXRecordDecl {{.*}} struct CheckFullExpression definition
+ NonTrivial value = NonTrivial();
+// CHECK: | |-FieldDecl {{.*}} value 'NonTrivial':'NonTrivial'
+// CHECK-NEXT: | | `-ExprWithCleanups {{.*}} 'NonTrivial':'NonTrivial'
+// CHECK-NEXT: | | `-CXXBindTemporaryExpr {{.*}} 'NonTrivial':'NonTrivial' (CXXTemporary{{.*}})
+// CHECK-NEXT: | | `-CXXTemporaryObjectExpr {{.*}} 'NonTrivial':'NonTrivial' 'void ()'
+};
+
+struct CheckNoCleanup {
+// CHECK: `-CXXRecordDecl {{.*}} struct CheckNoCleanup definition
+ static constexpr char kConstant = '+';
+// CHECK: `-VarDecl {{.*}} kConstant 'const char' static inline constexpr cinit
+// CHECK-NEXT: |-value: Int 43
+// CHECK-NEXT: `-CharacterLiteral {{.*}} 'char' 43
+};
diff --git a/clang/test/CXX/class/class.local/p1-0x.cpp b/clang/test/CXX/class/class.local/p1-0x.cpp
index 49125f5f9b062..096f5080099ec 100644
--- a/clang/test/CXX/class/class.local/p1-0x.cpp
+++ b/clang/test/CXX/class/class.local/p1-0x.cpp
@@ -11,8 +11,8 @@ void f() {
int x = 3; // expected-note{{'x' declared here}}
struct C {
int& x2 = x; // expected-error{{reference to local variable 'x' declared in enclosing lambda expression}}
- };
+ }c; // expected-note {{required here}}
};
- C();
+ C(); // expected-note {{required here}}
}
diff --git a/clang/test/CXX/drs/dr26xx.cpp b/clang/test/CXX/drs/dr26xx.cpp
index 987904913897b..36aea39824740 100644
--- a/clang/test/CXX/drs/dr26xx.cpp
+++ b/clang/test/CXX/drs/dr26xx.cpp
@@ -103,3 +103,19 @@ void f() {
brachiosaur |= neck; // OK
}
}
+
+namespace dr2631 { // dr2631: 16
+ constexpr int g();
+ consteval int f() {
+ return g();
+ }
+ int k(int x = f()) {
+ return x;
+ }
+ constexpr int g() {
+ return 42;
+ }
+ int test() {
+ return k();
+ }
+}
diff --git a/clang/test/CodeGenCXX/builtin-source-location.cpp b/clang/test/CodeGenCXX/builtin-source-location.cpp
index 6e44e6b0e60e3..7af6749d0d6d6 100644
--- a/clang/test/CodeGenCXX/builtin-source-location.cpp
+++ b/clang/test/CodeGenCXX/builtin-source-location.cpp
@@ -1,4 +1,6 @@
// RUN: %clang_cc1 -no-opaque-pointers -std=c++2a -fblocks %s -triple x86_64-unknown-unknown -emit-llvm -o %t.ll
+// RUN: %clang_cc1 -no-opaque-pointers -std=c++14 -fblocks %s -triple x86_64-unknown-unknown -emit-llvm -o %t.ll
+
// This needs to be performed before #line directives which alter filename
// RUN: %clang_cc1 -no-opaque-pointers -fno-file-reproducible -fmacro-prefix-map=%p=/UNLIKELY/PATH -emit-llvm -o - %s | FileCheck %s --check-prefix=CHECK-PREFIX-MAP
diff --git a/clang/test/CodeGenCXX/default-arguments-with-immediate.cpp b/clang/test/CodeGenCXX/default-arguments-with-immediate.cpp
new file mode 100644
index 0000000000000..54a02ffc06836
--- /dev/null
+++ b/clang/test/CodeGenCXX/default-arguments-with-immediate.cpp
@@ -0,0 +1,54 @@
+// RUN: %clang_cc1 -std=c++2a -triple x86_64-elf-gnu %s -emit-llvm -o - | FileCheck %s
+
+consteval int immediate() { return 0;}
+static int ext();
+void f(int a = immediate() + ext());
+
+void test_function() {
+ f();
+ f(0);
+ // CHECK: call noundef i32 @_ZL3extv()
+ // CHECK: add
+ // CHECK: call {{.*}} @_Z1fi
+ // CHECK: call {{.*}} @_Z1fi
+}
+
+// CHECK: define {{.*}} i32 @_ZL3extv()
+
+static constexpr int not_immediate();
+struct A {
+ int a = immediate() + not_immediate();
+};
+
+void test_member() {
+ // CHECK: call void @_ZN1AC2Ev
+ A defaulted;
+ // CHECK-NOT: call void @_ZN1AC2Ev
+ A provided{0};
+}
+
+// CHECK: define {{.*}} void @_ZN1AC2Ev{{.*}}
+// CHECK: %call = call noundef i32 @_ZL13not_immediatev()
+
+int never_referenced() {return 42;};
+
+
+namespace not_used {
+
+struct A {
+ int a = immediate() + never_referenced();
+};
+void f(int a = immediate() + never_referenced());
+
+void g() {
+ A a{0};
+ f(0);
+}
+
+}
+
+static int ext() {return 0;}
+static constexpr int not_immediate() {return 0;}
+
+// CHECK-NOT: define {{.*}} i32 _ZL16never_referencedv()(
+// CHECK: define {{.*}} i32 @_ZL13not_immediatev()
diff --git a/clang/test/CodeGenCXX/meminit-initializers-odr.cpp b/clang/test/CodeGenCXX/meminit-initializers-odr.cpp
new file mode 100644
index 0000000000000..b22b99db03a63
--- /dev/null
+++ b/clang/test/CodeGenCXX/meminit-initializers-odr.cpp
@@ -0,0 +1,140 @@
+// RUN: %clang_cc1 -std=c++20 %s -triple x86_64-linux-gnu -emit-llvm -o - | FileCheck %s
+
+struct ThisShouldBeCalled {
+ ThisShouldBeCalled() {}
+};
+
+template <typename T>
+struct ThisShouldBeCalledTPL {
+ ThisShouldBeCalledTPL() {}
+};
+
+consteval int f () {
+ return 42;
+}
+
+struct WithConsteval {
+ WithConsteval(int x = f()) {}
+};
+
+template <typename T>
+struct WithConstevalTPL {
+ WithConstevalTPL(T x = f()) {}
+};
+
+
+struct Base {
+ ThisShouldBeCalled y = {};
+};
+
+struct S : Base {
+ ThisShouldBeCalledTPL<int> A = {};
+ WithConsteval B = {};
+ WithConstevalTPL<double> C = {};
+};
+void Do(S = S{}) {}
+
+void test() {
+ Do();
+}
+
+// CHECK-LABEL: @_ZN18ThisShouldBeCalledC2Ev
+// CHECK-LABEL: @_ZN21ThisShouldBeCalledTPLIiEC2Ev
+// CHECK-LABEL: @_ZN13WithConstevalC2Ei
+// CHECK-LABEL: @_ZN16WithConstevalTPLIdEC2Ed
+
+namespace check_arrays {
+
+template <typename T>
+struct inner {
+ inner() {}
+};
+
+struct S {
+ inner<int> a {};
+};
+
+class C {
+ S s[1]{};
+};
+
+int f() {
+ C c;
+ return 0;
+}
+
+// CHECK-LABEL: @_ZN12check_arrays5innerIiEC2Ev
+
+}
+
+namespace check_field_inits_in_base_constructors {
+
+template <typename>
+struct ShouldBeODRUsed {
+ ShouldBeODRUsed() {}
+};
+class k {
+// The private here is important,
+// otherwise it would be aggregate initialized.
+private:
+ ShouldBeODRUsed<k> a = {};
+};
+
+struct b {
+ k c{};
+};
+void test() { b d; }
+
+// CHECK-LABEL: @_ZN38check_field_inits_in_base_constructors15ShouldBeODRUsedINS_1kEEC2Ev
+
+}
+
+namespace check_referenced_when_defined_in_default_parameter {
+
+template <typename T>
+struct Test {
+ Test(auto&&) {}
+};
+
+struct Options {
+ Test<bool(bool x)> identity = [](bool x) -> bool { return x; };
+};
+
+struct Wrapper {
+ Wrapper(const Options& options = Options());
+};
+
+void Func() { Options options; }
+
+// CHECK-LABEL: @_ZN50check_referenced_when_defined_in_default_parameter7OptionsC2Ev
+// CHECK-LABEL: @_ZN50check_referenced_when_defined_in_default_parameter4TestIFbbEEC1INS_7Options8identityMUlbE_EEEOT_
+// CHECK-LABEL: @_ZN50check_referenced_when_defined_in_default_parameter4TestIFbbEEC2INS_7Options8identityMUlbE_EEEOT_
+
+}
+
+namespace lambda_body {
+template <typename a>
+int templated_func() {
+ return 0;
+}
+struct test_body {
+ int mem = templated_func<int>();
+};
+struct test_capture {
+ int mem = templated_func<double>();
+};
+
+struct S {
+ int a = [_ = test_capture{}] { (void)test_body{}; return 0;}();
+};
+
+void test() {
+ S s;
+}
+
+// CHECK-LABEL: define{{.*}} @_ZN11lambda_body14templated_funcIdEEiv
+// CHECK-LABEL: define{{.*}} @_ZNK11lambda_body1S1aMUlvE_clEv
+// CHECK-LABEL: define{{.*}} @_ZN11lambda_body14templated_funcIiEEiv
+
+
+}
diff --git a/clang/test/PCH/default-argument-with-immediate-calls.cpp b/clang/test/PCH/default-argument-with-immediate-calls.cpp
new file mode 100644
index 0000000000000..510605a23d4e7
--- /dev/null
+++ b/clang/test/PCH/default-argument-with-immediate-calls.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -std=c++20 -emit-pch %s -o %t
+// RUN: %clang_cc1 -std=c++20 -include-pch %t -verify %s
+// expected-no-diagnostics
+
+#ifndef HEADER_INCLUDED
+#define HEADER_INCLUDED
+
+consteval int immediate();
+int regular_function() {
+ return 0;
+}
+
+struct S {
+ int a = immediate() + regular_function();
+};
+
+int f(int arg = immediate()) {
+ return arg;
+}
+
+#else
+
+consteval int immediate() {
+ return 0;
+}
+
+void test() {
+ f(0);
+ f();
+ S s{0};
+ S t{0};
+}
+
+#endif
diff --git a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp
index 9353e633fafbc..9c18c73be8f68 100644
--- a/clang/test/SemaCXX/cxx11-default-member-initializers.cpp
+++ b/clang/test/SemaCXX/cxx11-default-member-initializers.cpp
@@ -1,4 +1,6 @@
// RUN: %clang_cc1 -std=c++11 -verify %s -pedantic
+// RUN: %clang_cc1 -std=c++20 -verify %s -pedantic
+
namespace PR31692 {
struct A {
@@ -12,3 +14,95 @@ namespace PR31692 {
// A::X can now be default-constructed.
static_assert(__is_constructible(A::X), "");
}
+
+
+struct S {
+} constexpr s;
+struct C {
+ C(S);
+};
+class MemInit {
+ C m = s;
+};
+
+#if __cplusplus >= 202002L
+// This test ensures cleanup expressions are correctly produced
+// in the presence of default member initializers.
+namespace PR136554 {
+struct string {
+ constexpr string(const char*) {};
+ constexpr ~string();
+};
+struct S;
+struct optional {
+ template <typename U = S>
+ constexpr optional(U &&) {}
+};
+struct S {
+ string a;
+ optional b;
+ int defaulted = 0;
+} test {
+ "", {
+ { "", 0 }
+ }
+};
+
+// Ensure that the this pointer is
+// transformed without crashing
+consteval int immediate() { return 0;}
+struct StructWithThisInInitializer {
+ int member() const {
+ return 0;
+ }
+ int m = member() + immediate();
+ int m2 = this->member() + immediate();
+};
+
+template <typename T>
+struct StructWithThisInInitializerTPL {
+ template <typename U>
+ int member() const {
+ return 0;
+ }
+ int m = member<int>() + immediate();
+ int m2 = this->member<int>() + immediate();
+};
+
+void test_this() {
+ (void)StructWithThisInInitializer{};
+ (void)StructWithThisInInitializerTPL<int>{};
+}
+
+struct ReferenceToNestedMembers {
+ int m;
+ int a = ((void)immediate(), m); // ensure g is found in the correct scope
+ int b = ((void)immediate(), this->m); // ensure g is found in the correct scope
+};
+struct ReferenceToNestedMembersTest {
+ void* m = nullptr;
+ ReferenceToNestedMembers j{0};
+} test_reference_to_nested_members;
+
+}
+
+
+namespace odr_in_unevaluated_context {
+template <typename e, bool = __is_constructible(e)> struct f {
+ using type = bool;
+};
+
+template <class k, f<k>::type = false> int l;
+int m;
+struct p {
+ // This used to crash because m is first marked odr used
+ // during parsing, but subsequently used in an unevaluated context
+ // without being transformed.
+ int o = m;
+ p() {}
+};
+
+int i = l<p>;
+}
+
+#endif
diff --git a/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp b/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
new file mode 100644
index 0000000000000..abeb27fd03e35
--- /dev/null
+++ b/clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
@@ -0,0 +1,81 @@
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++20 %s
+// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2b %s
+
+consteval int undefined(); // expected-note 4 {{declared here}}
+
+void check_lambdas_unused(
+ int a = []
+ {
+ // The body of a lambda is not a subexpression of the lambda
+ // so this is immediately evaluated even if the parameter
+ // is never used.
+ return undefined(); // expected-error {{not a constant expression}} \
+ // expected-note {{undefined function 'undefined'}}
+ }(),
+ int b = [](int no_error = undefined()) {
+ return no_error;
+ }(0),
+ int c = [](int defaulted = undefined()) {
+ return defaulted;
+ }()
+) {}
+
+int check_lambdas_used(
+ int b = [](int no_error = undefined()) {
+ return no_error;
+ }(0),
+ int c = [](int defaulted = undefined()) { // expected-error {{not a constant expression}} \
+ // expected-note {{declared here}} \
+ // expected-note {{undefined function 'undefined'}}
+ return defaulted;
+ }(), // expected-note {{in the default initalizer of 'defaulted'}}
+ int d = [](int defaulted = sizeof(undefined())) {
+ return defaulted;
+ }()
+) {
+ return 0;
+}
+
+int test_check_lambdas_used = check_lambdas_used();
+
+struct UnusedInitWithLambda {
+ int a = [] {
+ return undefined(); // expected-error {{not a constant expression}} \
+ // expected-note {{undefined function 'undefined'}}
+ }();
+ // UnusedInitWithLambda is never constructed, so the initializer
+ // of b and undefined() are never evaluated.
+ int b = [](int no_error = undefined()) {
+ return no_error;
+ }();
+};
+
+consteval int ub(int n) {
+ return 0/n; // expected-note {{division}}
+}
+
+struct InitWithLambda {
+ int b = [](int error = undefined()) { // expected-error {{not a constant expression}} \
+ // expected-note {{declared here}} \
+ // expected-note {{undefined function 'undefined'}}
+ return error;
+ }(); // expected-note {{in the default initalizer of 'error'}}
+ int c = [](int error = sizeof(undefined()) + ub(0)) { // expected-error {{'ub' is not a constant expression}} \
+ // expected-note {{declared here}} \
+ // expected-note {{in call to 'ub(0)}}
+ return error;
+ }(); // expected-note {{in the default initalizer of 'error'}}
+} i; // expected-note {{in implicit default constructor}}
+
+namespace ShouldNotCrash {
+ template<typename T>
+ struct F {
+ template<typename U>
+ F(const U&) {}
+ };
+ struct A {
+ static constexpr auto x = [] {};
+ F<int> f = x;
+ };
+ void f(A a = A()) { }
+}
diff --git a/clang/test/SemaCXX/source_location.cpp b/clang/test/SemaCXX/source_location.cpp
index ccb385f60dc4b..f7c347ce357aa 100644
--- a/clang/test/SemaCXX/source_location.cpp
+++ b/clang/test/SemaCXX/source_location.cpp
@@ -1,4 +1,5 @@
// RUN: %clang_cc1 -std=c++1z -fcxx-exceptions -fexceptions -verify %s
+// RUN: %clang_cc1 -std=c++2a -fcxx-exceptions -DUSE_CONSTEVAL -fexceptions -verify %s
// expected-no-diagnostics
#define assert(...) ((__VA_ARGS__) ? ((void)0) : throw 42)
@@ -8,15 +9,22 @@
template <unsigned>
struct Printer;
+#ifdef USE_CONSTEVAL
+#define SOURCE_LOC_EVAL_KIND consteval
+#else
+#define SOURCE_LOC_EVAL_KIND constexpr
+#endif
+
namespace std {
class source_location {
struct __impl;
public:
- static constexpr source_location current(const __impl *__p = __builtin_source_location()) noexcept {
- source_location __loc;
- __loc.__m_impl = __p;
- return __loc;
+ static SOURCE_LOC_EVAL_KIND source_location
+ current(const __impl *__p = __builtin_source_location()) noexcept {
+ source_location __loc;
+ __loc.__m_impl = __p;
+ return __loc;
}
constexpr source_location() = default;
constexpr source_location(source_location const &) = default;
@@ -593,3 +601,73 @@ namespace TestConstexprContext {
}
static_assert(test());
}
+
+namespace Lambda {
+#line 8000 "TestLambda.cpp"
+constexpr int nested_lambda(int l = []{
+ return SL::current().line();
+}()) {
+ return l;
+}
+static_assert(nested_lambda() == __LINE__ - 4);
+
+constexpr int lambda_param(int l = [](int l = SL::current().line()) {
+ return l;
+}()) {
+ return l;
+}
+static_assert(lambda_param() == __LINE__);
+
+
+}
+
+constexpr int compound_literal_fun(int a =
+ (int){ SL::current().line() }
+) { return a ;}
+static_assert(compound_literal_fun() == __LINE__);
+
+struct CompoundLiteral {
+ int a = (int){ SL::current().line() };
+};
+static_assert(CompoundLiteral{}.a == __LINE__);
+
+
+// FIXME
+// Init captures are subexpressions of the lambda expression
+// so according to the standard immediate invocations in init captures
+// should be evaluated at the call site.
+// However Clang does not yet implement this as it would introduce
+// a fair bit of complexity.
+// We intend to implement that functionality once we find real world
+// use cases that require it.
+constexpr int test_init_capture(int a =
+ [b = SL::current().line()] { return b; }()) {
+ return a;
+}
+#ifdef USE_CONSTEVAL
+static_assert(test_init_capture() == __LINE__ - 4);
+#else
+static_assert(test_init_capture() == __LINE__ );
+#endif
+
+namespace check_immediate_invocations_in_templates {
+
+template <typename T = int>
+struct G {
+ T line = __builtin_LINE();
+};
+template <typename T>
+struct S {
+ int i = G<T>{}.line;
+};
+static_assert(S<int>{}.i != // intentional new line
+ S<int>{}.i);
+
+template <typename T>
+constexpr int f(int i = G<T>{}.line) {
+ return i;
+}
+
+static_assert(f<int>() != // intentional new line
+ f<int>());
+}
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index fe609ea08a63d..0c621a7894340 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -15593,7 +15593,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
<td><a href="https://wg21.link/cwg2631">2631</a></td>
<td>DR</td>
<td>Immediate function evaluations in default arguments</td>
- <td class="none" align="center">Unknown</td>
+ <td class="unreleased" align="center">Clang 16</td>
</tr>
<tr class="open" id="2632">
<td><a href="https://wg21.link/cwg2632">2632</a></td>
More information about the cfe-commits
mailing list