diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 0faa5aed4eec3..b0dd4494b7c3b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5287,7 +5287,7 @@ class Sema final : public SemaBase { /// is complete. void ActOnFinishCXXInClassMemberInitializer(Decl *VarDecl, SourceLocation EqualLoc, - Expr *Init); + ExprResult Init); /// Handle a C++ member initializer using parentheses syntax. MemInitResult diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp b/clang/lib/Parse/ParseCXXInlineMethods.cpp index b461743833c82..79e61af5f0685 100644 --- a/clang/lib/Parse/ParseCXXInlineMethods.cpp +++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp @@ -722,8 +722,7 @@ void Parser::ParseLexedMemberInitializer(LateParsedMemberInitializer &MI) { ExprResult Init = ParseCXXMemberInitializer(MI.Field, /*IsFunction=*/false, EqualLoc); - Actions.ActOnFinishCXXInClassMemberInitializer(MI.Field, EqualLoc, - Init.get()); + Actions.ActOnFinishCXXInClassMemberInitializer(MI.Field, EqualLoc, Init); // The next token should be our artificial terminating EOF token. if (Tok.isNot(tok::eof)) { diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index 38f808a470aa8..19acac7bf6d09 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -4080,24 +4080,28 @@ ExprResult Sema::ConvertMemberDefaultInitExpression(FieldDecl *FD, void Sema::ActOnFinishCXXInClassMemberInitializer(Decl *D, SourceLocation InitLoc, - Expr *InitExpr) { + ExprResult InitExpr) { // Pop the notional constructor scope we created earlier. PopFunctionScopeInfo(nullptr, D); - FieldDecl *FD = dyn_cast(D); - assert((isa(D) || FD->getInClassInitStyle() != ICIS_NoInit) && - "must set init style when field is created"); - - if (!InitExpr) { + // Microsoft C++'s property declaration cannot have a default member + // initializer. + if (isa(D)) { D->setInvalidDecl(); - if (FD) - FD->removeInClassInitializer(); return; } - if (DiagnoseUnexpandedParameterPack(InitExpr, UPPC_Initializer)) { + FieldDecl *FD = dyn_cast(D); + assert((FD && FD->getInClassInitStyle() != ICIS_NoInit) && + "must set init style when field is created"); + + if (!InitExpr.isUsable() || + DiagnoseUnexpandedParameterPack(InitExpr.get(), UPPC_Initializer)) { FD->setInvalidDecl(); - FD->removeInClassInitializer(); + ExprResult RecoveryInit = + CreateRecoveryExpr(InitLoc, InitLoc, {}, FD->getType()); + if (RecoveryInit.isUsable()) + FD->setInClassInitializer(RecoveryInit.get()); return; } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 044f56f2af71b..d2ffe610716ae 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -5563,10 +5563,6 @@ ExprResult Sema::BuildCXXDefaultArgExpr(SourceLocation CallLoc, 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(); - CXXThisScopeRAII This(*this, Field->getParent(), Qualifiers()); auto *ParentRD = cast(Field->getParent()); diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index f560865681fa5..7094a8c3bf120 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -750,6 +750,7 @@ void InitListChecker::FillInEmptyInitForField(unsigned Init, FieldDecl *Field, if (Field->hasInClassInitializer()) { if (VerifyOnly) return; + ExprResult DIE; { // Enter a default initializer rebuild context, then we can support diff --git a/clang/test/AST/ast-dump-recovery.cpp b/clang/test/AST/ast-dump-recovery.cpp index a88dff471d9f0..62e14c74f2865 100644 --- a/clang/test/AST/ast-dump-recovery.cpp +++ b/clang/test/AST/ast-dump-recovery.cpp @@ -460,3 +460,37 @@ void RecoveryForStmtCond() { // CHECK-NEXT: `-CompoundStmt {{.*}} for (int i = 0; i < invalid; ++i) {} } + +// Fix crash issue https://github.com/llvm/llvm-project/issues/112560. +// Make sure clang compiles the following code without crashing: + +// CHECK:NamespaceDecl {{.*}} GH112560 +// CHECK-NEXT: |-CXXRecordDecl {{.*}} referenced union U definition +// CHECK-NEXT: | |-DefinitionData {{.*}} +// CHECK-NEXT: | | |-DefaultConstructor {{.*}} +// CHECK-NEXT: | | |-CopyConstructor {{.*}} +// CHECK-NEXT: | | |-MoveConstructor {{.*}} +// CHECK-NEXT: | | |-CopyAssignment {{.*}} +// CHECK-NEXT: | | |-MoveAssignment {{.*}} +// CHECK-NEXT: | | `-Destructor {{.*}} +// CHECK-NEXT: | |-CXXRecordDecl {{.*}} implicit union U +// CHECK-NEXT: | `-FieldDecl {{.*}} invalid f 'int' +// CHECK-NEXT: | `-RecoveryExpr {{.*}} 'int' contains-errors +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +namespace GH112560 { +union U { + int f = ; +}; + +// CHECK: FunctionDecl {{.*}} foo 'void ()' +// CHECK-NEXT: `-CompoundStmt {{.*}} +// CHECK-NEXT: `-DeclStmt {{.*}} +// CHECK-NEXT: `-VarDecl {{.*}} g 'U':'GH112560::U' listinit +// CHECK-NEXT: `-InitListExpr {{.*}} 'U':'GH112560::U' contains-errors field Field {{.*}} 'f' 'int' +// CHECK-NEXT: `-CXXDefaultInitExpr {{.*}} 'int' contains-errors has rewritten init +// CHECK-NEXT: `-RecoveryExpr {{.*}} 'int' contains-errors +// DISABLED-NOT: -RecoveryExpr {{.*}} contains-errors +void foo() { + U g{}; +} +} // namespace GH112560 diff --git a/clang/test/SemaCXX/cxx1y-initializer-aggregates.cpp b/clang/test/SemaCXX/cxx1y-initializer-aggregates.cpp index 03a6800898d18..8360b8fd7d8ee 100644 --- a/clang/test/SemaCXX/cxx1y-initializer-aggregates.cpp +++ b/clang/test/SemaCXX/cxx1y-initializer-aggregates.cpp @@ -115,3 +115,14 @@ namespace nested_union { // of Test3, or we should exclude f(Test3) as a candidate. static_assert(f({1}) == 2, ""); // expected-error {{call to 'f' is ambiguous}} } + +// Fix crash issue https://github.com/llvm/llvm-project/issues/112560. +// Make sure clang compiles the following code without crashing: +namespace GH112560 { +union U { + int f = ; // expected-error {{expected expression}} +}; +void foo() { + U g{}; +} +} // namespace GH112560