cor3ntin updated this revision to Diff 470154.
cor3ntin added a comment.
Complete implementation for default member initializers,
tests, cleaups, docs, etc.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D136554/new/
https://reviews.llvm.org/D136554
Files:
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/ExprCXX.cpp
clang/lib/AST/ItaniumMangle.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/Serialization/ASTReaderStmt.cpp
clang/lib/Serialization/ASTWriterStmt.cpp
clang/test/CXX/class/class.local/p1-0x.cpp
clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
Index: clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
===================================================================
--- /dev/null
+++ clang/test/SemaCXX/cxx2a-consteval-default-params.cpp
@@ -0,0 +1,52 @@
+// 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 = []
+ {
+ 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'}}
+) {
+ 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'}}
+ }();
+ int b = [](int no_error = undefined()) {
+ return no_error;
+ }();
+};
+
+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'}}
+} i; // expected-note {{in implicit default constructor}}
Index: clang/test/CXX/class/class.local/p1-0x.cpp
===================================================================
--- clang/test/CXX/class/class.local/p1-0x.cpp
+++ clang/test/CXX/class/class.local/p1-0x.cpp
@@ -11,8 +11,8 @@
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}}
}
Index: clang/lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTWriterStmt.cpp
+++ clang/lib/Serialization/ASTWriterStmt.cpp
@@ -1748,6 +1748,9 @@
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;
}
@@ -1756,6 +1759,9 @@
Record.AddDeclRef(E->getField());
Record.AddDeclRef(cast_or_null<Decl>(E->getUsedContext()));
Record.AddSourceLocation(E->getExprLoc());
+ Record.push_back(E->hasRewrittenInit());
+ if (E->hasRewrittenInit())
+ Record.AddStmt(E->getRewrittenExpr());
Code = serialization::EXPR_CXX_DEFAULT_INIT;
}
Index: clang/lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderStmt.cpp
+++ clang/lib/Serialization/ASTReaderStmt.cpp
@@ -1828,6 +1828,9 @@
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) {
@@ -1835,6 +1838,9 @@
E->Field = readDeclAs<FieldDecl>();
E->UsedContext = readDeclAs<DeclContext>();
E->CXXDefaultInitExprBits.Loc = readSourceLocation();
+ E->CXXDefaultInitExprBits.HasRewrittenInit = Record.readInt();
+ if (E->CXXDefaultInitExprBits.HasRewrittenInit)
+ *E->getTrailingObjects<Expr *>() = Record.readSubExpr();
}
void ASTStmtReader::VisitCXXBindTemporaryExpr(CXXBindTemporaryExpr *E) {
@@ -3821,11 +3827,13 @@
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:
Index: clang/lib/Sema/TreeTransform.h
===================================================================
--- clang/lib/Sema/TreeTransform.h
+++ clang/lib/Sema/TreeTransform.h
@@ -3164,9 +3164,10 @@
/// By default, builds a new default-argument expression, which does not
/// require any semantic analysis. Subclasses may override this routine to
/// provide different 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.
@@ -3176,8 +3177,7 @@
/// routine to provide different 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.
@@ -12052,11 +12052,20 @@
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>
Index: clang/lib/Sema/SemaTemplateInstantiate.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -1927,9 +1927,9 @@
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>
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -5856,8 +5856,10 @@
}
bool Sema::CheckCXXDefaultArgExpr(SourceLocation CallLoc, FunctionDecl *FD,
- ParmVarDecl *Param) {
+ ParmVarDecl *Param, Expr *Init,
+ bool SkipImmediateInvocations) {
if (Param->hasUnparsedDefaultArg()) {
+ assert(!Init && "Should not have an 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)) {
@@ -5874,11 +5876,16 @@
return true;
}
- if (Param->hasUninstantiatedDefaultArg() &&
- InstantiateDefaultArgument(CallLoc, FD, Param))
- return true;
+ if (Param->hasUninstantiatedDefaultArg()) {
+ assert(!Init && "Should not have an init expression yet");
+ if (InstantiateDefaultArgument(CallLoc, FD, Param))
+ return true;
+ }
+ if (!Init) {
+ Init = Param->getInit();
+ }
- assert(Param->hasInit() && "default argument but no initializer?");
+ 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
@@ -5887,34 +5894,172 @@
// 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().IsCheckingDefaultArgumentOrInitializer =
+ SkipImmediateInvocations;
+ MarkDeclarationsReferencedInExpr(Init, true);
return false;
}
+struct ImmediateCallVisitor : public RecursiveASTVisitor<ImmediateCallVisitor> {
+ bool HasImmediateCalls = false;
+ bool VisitCallExpr(CallExpr *E) {
+ if (const FunctionDecl *FD =
+ dyn_cast_or_null<FunctionDecl>(E->getCalleeDecl())) {
+ HasImmediateCalls = HasImmediateCalls || FD->isConsteval();
+ }
+ return RecursiveASTVisitor<ImmediateCallVisitor>::VisitStmt(E);
+ }
+
+ bool VisitSourceLocExpr(SourceLocExpr *E) {
+ HasImmediateCalls = true;
+ return RecursiveASTVisitor<ImmediateCallVisitor>::VisitStmt(E);
+ }
+
+ bool VisitLambdaExpr(LambdaExpr *E) {
+ return VisitCXXMethodDecl(E->getCallOperator());
+ }
+
+ bool VisitParmVarDecl(ParmVarDecl *D) { return VisitExpr(D->getInit()); }
+};
+
+struct EnsureImmediateInvocationInDefaultArgs
+ : TreeTransform<EnsureImmediateInvocationInDefaultArgs> {
+ EnsureImmediateInvocationInDefaultArgs(Sema &SemaRef)
+ : TreeTransform(SemaRef) {}
+ ExprResult TransformLambdaExpr(LambdaExpr *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();
+
+ if (!Init && !Param->hasUnparsedDefaultArg() &&
+ !Param->hasUninstantiatedDefaultArg()) {
+ ImmediateCallVisitor V;
+ if (!NestedDefaultChecking)
+ V.TraverseDecl(Param);
+ if (V.HasImmediateCalls) {
+ EnterExpressionEvaluationContext EvalContext(
+ *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated,
+ Param);
+ EnsureImmediateInvocationInDefaultArgs Immediate(*this);
+ ExprEvalContexts.back().DelayedDefaultInitializationContext = {Param,
+ CallLoc};
+ ExprResult Res = Immediate.TransformExpr(Param->getInit());
+ 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, CallLoc, Param, CurContext);
+
+ return CXXDefaultArgExpr::Create(Context, CallLoc, Param, Init, CurContext);
+}
+
+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();
+
+ CXXRecordDecl *ParentRD = cast<CXXRecordDecl>(Field->getParent());
+
+ 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 (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();
+ }
+ }
+ }
+ Expr *Init = nullptr;
+ if (Field->getInClassInitializer()) {
+ ImmediateCallVisitor V;
+ V.TraverseDecl(Field);
+ EnterExpressionEvaluationContext EvalContext(
+ *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, Field);
+ if (V.HasImmediateCalls) {
+ ExprEvalContexts.back().DelayedDefaultInitializationContext = {Field,
+ Loc};
+ EnsureImmediateInvocationInDefaultArgs Immediate(*this);
+ ExprResult Res = Immediate.TransformExpr(Field->getInClassInitializer());
+ if (!Res.isInvalid())
+ Res = ConvertMemberDefaultInitExpression(Field, Res.get(), Loc);
+ if (Res.isInvalid()) {
+ Field->setInvalidDecl();
+ return ExprError();
+ }
+ Init = Res.get();
+ } else {
+ MarkDeclarationsReferencedInExpr(Field->getInClassInitializer());
+ }
+
+ return CXXDefaultInitExpr::Create(Context, Loc, Field, CurContext, 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
@@ -17539,6 +17684,7 @@
ExprResult Sema::CheckForImmediateInvocation(ExprResult E, FunctionDecl *Decl) {
if (isUnevaluatedContext() || !E.isUsable() || !Decl ||
!Decl->isConsteval() || isConstantEvaluated() ||
+ isCheckingDefaultArgumentOrInitializer() ||
RebuildingImmediateInvocation || isImmediateFunctionContext())
return E;
@@ -17586,6 +17732,12 @@
llvm_unreachable("unhandled decl kind");
assert(FD->isConsteval());
SemaRef.Diag(CE->getBeginLoc(), diag::err_invalid_consteval_call) << FD;
+ if (auto [D, Loc] =
+ SemaRef.InnermostDeclarationWithDelayedImmediateInvocations();
+ D) {
+ SemaRef.Diag(Loc, diag::note_invalid_consteval_initializer) << D;
+ SemaRef.Diag(D->getBeginLoc(), diag::note_declared_at);
+ }
for (auto &Note : Notes)
SemaRef.Diag(Note.first, Note.second);
return;
@@ -18818,7 +18970,6 @@
}
}
-
// If the variable is declared in the current context, there is no need to
// capture it.
if (VarDC == DC) return true;
@@ -19731,7 +19882,8 @@
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,
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -4025,6 +4025,21 @@
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.
@@ -4053,16 +4068,7 @@
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);
if (Init.isInvalid()) {
FD->setInvalidDecl();
return;
@@ -15598,70 +15604,6 @@
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
Index: clang/lib/Parse/ParseDeclCXX.cpp
===================================================================
--- clang/lib/Parse/ParseDeclCXX.cpp
+++ clang/lib/Parse/ParseDeclCXX.cpp
@@ -3184,7 +3184,11 @@
"Data member initializer not starting with '=' or '{'");
EnterExpressionEvaluationContext Context(
- Actions, Sema::ExpressionEvaluationContext::PotentiallyEvaluated, D);
+ Actions,
+ isa_and_nonnull<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
Index: clang/lib/AST/ItaniumMangle.cpp
===================================================================
--- clang/lib/AST/ItaniumMangle.cpp
+++ clang/lib/AST/ItaniumMangle.cpp
@@ -4383,7 +4383,7 @@
}
case Expr::CXXDefaultArgExprClass:
- E = cast<CXXDefaultArgExpr>(E)->getExpr();
+ E = cast<CXXDefaultArgExpr>(E)->getParam()->getDefaultArg();
goto recurse;
case Expr::CXXDefaultInitExprClass:
Index: clang/lib/AST/ExprCXX.cpp
===================================================================
--- clang/lib/AST/ExprCXX.cpp
+++ clang/lib/AST/ExprCXX.cpp
@@ -949,9 +949,59 @@
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 ? 1 : 0);
+ auto *Mem = C.Allocate(Size, alignof(CXXDefaultArgExpr));
+ return new (Mem) CXXDefaultArgExpr(CXXDefaultArgExprClass, Loc, Param,
+ RewrittenExpr, UsedContext);
+}
+
+const Expr *CXXDefaultArgExpr::getExpr() const {
+ return CXXDefaultArgExprBits.HasRewrittenInit ? getAdjustedRewrittenExpr()
+ : getParam()->getDefaultArg();
+}
+
+Expr *CXXDefaultArgExpr::getExpr() {
+ return CXXDefaultArgExprBits.HasRewrittenInit ? getAdjustedRewrittenExpr()
+ : getParam()->getDefaultArg();
+}
+
+const Expr *CXXDefaultArgExpr::getAdjustedRewrittenExpr() const {
+ if (!hasRewrittenInit())
+ return nullptr;
+ const Expr *Init = getRewrittenExpr();
+
+ if (auto *E = dyn_cast_or_null<FullExpr>(Init))
+ if (!isa<ConstantExpr>(E))
+ return E->getSubExpr();
+ return Init;
+}
+
+Expr *CXXDefaultArgExpr::getAdjustedRewrittenExpr() {
+ if (!hasRewrittenInit())
+ return nullptr;
+ Expr *Init = getRewrittenExpr();
+ if (auto *E = dyn_cast_or_null<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 +1009,50 @@
/*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 ? 1 : 0);
+ auto *Mem = Ctx.Allocate(Size, alignof(CXXDefaultArgExpr));
+ return new (Mem) CXXDefaultInitExpr(Ctx, Loc, Field, Field->getType(),
+ UsedContext, RewrittenInitExpr);
+}
+
+/// Get the initialization expression that will be used.
+const Expr *CXXDefaultInitExpr::getExpr() const {
+ assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
+ if (hasRewrittenInit())
+ return getRewrittenExpr();
+
+ return Field->getInClassInitializer();
+}
+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);
Index: clang/lib/AST/ASTImporter.cpp
===================================================================
--- clang/lib/AST/ASTImporter.cpp
+++ clang/lib/AST/ASTImporter.cpp
@@ -7691,9 +7691,16 @@
if (Error Err = ImportDefaultArgOfParmVarDecl(*FromParam, ToParam))
return std::move(Err);
}
-
+ Expr *RewrittenInit = nullptr;
+ if (E->hasRewrittenInit()) {
+ ExpectedExpr ExprOrErr = import(E->getExpr());
+ if (!ExprOrErr)
+ return ExprOrErr.takeError();
+ RewrittenInit = ExprOrErr.get();
+ }
return CXXDefaultArgExpr::Create(Importer.getToContext(), *ToUsedLocOrErr,
- *ToParamOrErr, *UsedContextOrErr);
+ *ToParamOrErr, RewrittenInit,
+ *UsedContextOrErr);
}
ExpectedStmt
@@ -8387,8 +8394,16 @@
ToField->setInClassInitializer(*ToInClassInitializerOrErr);
}
+ Expr *RewrittenInit = nullptr;
+ if (E->hasRewrittenInit()) {
+ ExpectedExpr ExprOrErr = import(E->getExpr());
+ if (!ExprOrErr)
+ return ExprOrErr.takeError();
+ RewrittenInit = ExprOrErr.get();
+ }
+
return CXXDefaultInitExpr::Create(Importer.getToContext(), *ToBeginLocOrErr,
- ToField, *UsedContextOrErr);
+ ToField, *UsedContextOrErr, RewrittenInit);
}
ExpectedStmt ASTNodeImporter::VisitCXXNamedCastExpr(CXXNamedCastExpr *E) {
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -1330,6 +1330,13 @@
bool InDiscardedStatement;
bool InImmediateFunctionContext;
+ bool IsCheckingDefaultArgumentOrInitializer = false;
+ // When evaluating immediate functions in the initializer of a default
+ // parameter or default member initializer, this is is the declaration whose
+ // default initializer is being evaluated and the location of the call
+ std::pair<ValueDecl *, SourceLocation> DelayedDefaultInitializationContext =
+ {nullptr, {}};
+
ExpressionEvaluationContextRecord(ExpressionEvaluationContext Context,
unsigned NumCleanupObjects,
CleanupInfo ParentCleanup,
@@ -6195,19 +6202,22 @@
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.
@@ -9573,6 +9583,22 @@
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.IsCheckingDefaultArgumentOrInitializer;
+ }
+
+ std::pair<ValueDecl *, SourceLocation>
+ InnermostDeclarationWithDelayedImmediateInvocations() const {
+ assert(!ExprEvalContexts.empty() &&
+ "Must be in an expression evaluation context");
+ return ExprEvalContexts.back().DelayedDefaultInitializationContext;
+ }
+
/// RAII class used to determine whether SFINAE has
/// trapped any errors that occur during template argument
/// deduction.
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2644,6 +2644,10 @@
" 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<
Index: clang/include/clang/AST/Stmt.h
===================================================================
--- clang/include/clang/AST/Stmt.h
+++ clang/include/clang/AST/Stmt.h
@@ -686,6 +686,9 @@
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;
};
@@ -696,6 +699,10 @@
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;
};
Index: clang/include/clang/AST/ExprCXX.h
===================================================================
--- clang/include/clang/AST/ExprCXX.h
+++ clang/include/clang/AST/ExprCXX.h
@@ -1245,8 +1245,12 @@
/// 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;
@@ -1255,7 +1259,7 @@
DeclContext *UsedContext;
CXXDefaultArgExpr(StmtClass SC, SourceLocation Loc, ParmVarDecl *Param,
- DeclContext *UsedContext)
+ Expr *RewrittenExpr, DeclContext *UsedContext)
: Expr(SC,
Param->hasUnparsedDefaultArg()
? Param->getType().getNonReferenceType()
@@ -1264,28 +1268,51 @@
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;
+ }
+
+ size_t numTrailingObjects() const {
+ return CXXDefaultArgExprBits.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.
+ const Expr *getExpr() const;
+ Expr *getExpr();
+
+ const Expr *getRewrittenExpr() const {
+ return hasRewrittenInit() ? *getTrailingObjects<Expr *>() : nullptr;
+ }
+ Expr *getRewrittenExpr() {
+ return hasRewrittenInit() ? *getTrailingObjects<Expr *>() : nullptr;
+ }
+
+ const Expr *getAdjustedRewrittenExpr() const;
+ Expr *getAdjustedRewrittenExpr();
const DeclContext *getUsedContext() const { return UsedContext; }
DeclContext *getUsedContext() { return UsedContext; }
@@ -1322,10 +1349,13 @@
/// 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<CXXDefaultArgExpr, Expr *> {
+ friend class ASTStmtReader;
+ friend class ASTReader;
+ friend TrailingObjects;
/// The field whose default is being used.
FieldDecl *Field;
@@ -1333,16 +1363,29 @@
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;
+ }
+
+ size_t numTrailingObjects() const {
+ return CXXDefaultInitExprBits.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.
@@ -1350,13 +1393,15 @@
const FieldDecl *getField() const { return Field; }
/// Get the initialization expression that will be used.
- const Expr *getExpr() const {
- assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
- return Field->getInClassInitializer();
+ const Expr *getExpr() const;
+ Expr *getExpr();
+
+ const Expr *getRewrittenExpr() const {
+ return hasRewrittenInit() ? *getTrailingObjects<Expr *>() : nullptr;
}
- Expr *getExpr() {
- assert(Field->getInClassInitializer() && "initializer hasn't been parsed");
- return Field->getInClassInitializer();
+
+ Expr *getRewrittenExpr() {
+ return hasRewrittenInit() ? *getTrailingObjects<Expr *>() : nullptr;
}
const DeclContext *getUsedContext() const { return UsedContext; }
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -504,6 +504,8 @@
This means Clang will by default accept code using features from C++17 and
conforming GNU extensions. Projects incompatible with C++17 can add
``-std=gnu++14`` to their build settings to restore the previous behaviour.
+- Implemented DR2631. Invalid ``consteval`` calls in default arguments and default
+ member initializers are diagnosed when and if the default is used.
C++20 Feature Support
^^^^^^^^^^^^^^^^^^^^^
@@ -651,8 +653,8 @@
the behavior of ``QualType::getNonReferenceType`` for ``CXType``.
- Introduced the new function ``clang_CXXMethod_isDeleted``, which queries
whether the method is declared ``= delete``.
-- ``clang_Cursor_getNumTemplateArguments``, ``clang_Cursor_getTemplateArgumentKind``,
- ``clang_Cursor_getTemplateArgumentType``, ``clang_Cursor_getTemplateArgumentValue`` and
+- ``clang_Cursor_getNumTemplateArguments``, ``clang_Cursor_getTemplateArgumentKind``,
+ ``clang_Cursor_getTemplateArgumentType``, ``clang_Cursor_getTemplateArgumentValue`` and
``clang_Cursor_getTemplateArgumentUnsignedValue`` now work on struct, class,
and partial template specialization cursors in addition to function cursors.
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits