diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -4464,7 +4464,8 @@ "function (the implicit copy assignment operator)|" "function (the implicit move assignment operator)|" "function (the implicit 'operator==' for this 'operator<=>)'|" - "inherited constructor}0%select{| template| %2}1">; + "inherited constructor|" + "aggregate parenthesis initializer}0%select{| template| %2}1">; def note_ovl_candidate : Note< "candidate %sub{select_ovl_candidate_kind}0,1,3" diff --git a/clang/include/clang/Sema/Initialization.h b/clang/include/clang/Sema/Initialization.h --- a/clang/include/clang/Sema/Initialization.h +++ b/clang/include/clang/Sema/Initialization.h @@ -923,7 +923,13 @@ SK_OCLSamplerInit, /// Initialize an opaque OpenCL type (event_t, queue_t, etc.) with zero - SK_OCLZeroOpaqueType + SK_OCLZeroOpaqueType, + + /// Array initialization from a parenthesized list: int a[](1, 2, 3) (C++20) + SK_ArrayParenthesizedInit, + + /// Initialize an aggregate from a parenthesized list (C++20) + SK_AggregateParenthesizedInit, }; /// A single step in the initialization sequence. @@ -1099,6 +1105,9 @@ /// List-copy-initialization chose an explicit constructor. FK_ExplicitConstructor, + + /// Array parenthesized initialization failed. + FK_ArrayParenthesizedInitFailed, }; private: @@ -1200,6 +1209,11 @@ const InitializationKind &Kind, ArrayRef Args); + /// Add diagnostics for failure in aggregate parenthesis initialization, + /// shared between constructor overload and cast diagnostics. + void DiagnoseFailedAggregateParenthesizedInitialization( + Sema &S, const InitializedEntity &Entity, ArrayRef Args) const; + /// Determine the kind of initialization sequence computed. enum SequenceKind getKind() const { return SequenceKind; } @@ -1357,6 +1371,12 @@ /// from a zero constant. void AddOCLZeroOpaqueTypeStep(QualType T); + /// Add an array parenthesized initialization step. + void AddArrayParenthesizedInitStep(QualType T); + + /// Add an aggregate parenthesized initialization step. + void AddAggregateParenthesizedInitStep(QualType T); + /// Add steps to unwrap a initializer list for a reference around a /// single element and rewrap it at the end. void RewrapReferenceInitList(QualType T, InitListExpr *Syntactic); diff --git a/clang/lib/Frontend/InitPreprocessor.cpp b/clang/lib/Frontend/InitPreprocessor.cpp --- a/clang/lib/Frontend/InitPreprocessor.cpp +++ b/clang/lib/Frontend/InitPreprocessor.cpp @@ -673,7 +673,7 @@ // C++20 features. if (LangOpts.CPlusPlus20) { - // Builder.defineMacro("__cpp_aggregate_paren_init", "201902L"); + Builder.defineMacro("__cpp_aggregate_paren_init", "201902L"); // P0848 is implemented, but we're still waiting for other concepts // issues to be addressed before bumping __cpp_concepts up to 202002L. diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -486,6 +486,9 @@ << src->getSourceRange()), S, howManyCandidates, src); + if (S.getLangOpts().CPlusPlus20 && destType->isAggregateType()) + sequence.DiagnoseFailedAggregateParenthesizedInitialization(S, entity, src); + return true; } diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -563,7 +563,7 @@ // assembling an argument list q_1 ... q_n . If a viable constructor is // found ([over.match.viable]), then promise-constructor-arguments is ( q_1 // , ..., q_n ), otherwise promise-constructor-arguments is empty. - if (InitSeq) { + if (InitSeq && InitSeq.isConstructorInitialization()) { ExprResult Result = InitSeq.Perform(*this, Entity, Kind, CtorArgExprs); if (Result.isInvalid()) { VD->setInvalidDecl(); diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -1904,17 +1904,23 @@ Initializer); } -static bool isLegalArrayNewInitializer(CXXNewExpr::InitializationStyle Style, +static bool isLegalArrayNewInitializer(Sema &S, + CXXNewExpr::InitializationStyle Style, Expr *Init) { if (!Init) return true; if (ParenListExpr *PLE = dyn_cast(Init)) - return PLE->getNumExprs() == 0; + return S.getLangOpts().CPlusPlus20 || + llvm::all_of(PLE->exprs(), + [](Expr *E) { return isa(E); }); if (isa(Init)) return true; else if (CXXConstructExpr *CCE = dyn_cast(Init)) return !CCE->isListInitialization() && - CCE->getConstructor()->isDefaultConstructor(); + (S.getLangOpts().CPlusPlus20 || + CCE->getConstructor()->isDefaultConstructor() || + llvm::all_of(CCE->arguments(), + [](Expr *E) { return isa(E); })); else if (Style == CXXNewExpr::ListInit) { assert(isa(Init) && "Shouldn't create list CXXConstructExprs for arrays."); @@ -2353,7 +2359,7 @@ // Array 'new' can't have any initializers except empty parentheses. // Initializer lists are also allowed, in C++11. Rely on the parser for the // dialect distinction. - if (ArraySize && !isLegalArrayNewInitializer(initStyle, Initializer)) { + if (ArraySize && !isLegalArrayNewInitializer(*this, initStyle, Initializer)) { SourceRange InitRange(Exprs.front()->getBeginLoc(), Exprs.back()->getEndLoc()); Diag(StartLoc, diag::err_new_array_init_args) << InitRange; @@ -5332,7 +5338,9 @@ // Make sure the first argument is not incomplete nor a function type. QualType T = Args[0]->getType(); - if (T->isIncompleteType() || T->isFunctionType()) + if ((T->isIncompleteType() || T->isFunctionType()) && + !(S.getLangOpts().CPlusPlus20 && T->isIncompleteArrayType() && + Args.size() > 1)) return false; // Make sure the first argument is not an abstract type. diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -3519,6 +3519,8 @@ case SK_StdInitializerListConstructorCall: case SK_OCLSamplerInit: case SK_OCLZeroOpaqueType: + case SK_ArrayParenthesizedInit: + case SK_AggregateParenthesizedInit: break; case SK_ConversionSequence: @@ -3578,6 +3580,7 @@ case FK_PlaceholderType: case FK_ExplicitConstructor: case FK_AddressOfUnaddressableFunction: + case FK_ArrayParenthesizedInitFailed: return false; case FK_ReferenceInitOverloadFailed: @@ -3813,6 +3816,20 @@ Steps.push_back(S); } +void InitializationSequence::AddArrayParenthesizedInitStep(QualType T) { + Step S; + S.Kind = SK_ArrayParenthesizedInit; + S.Type = T; + Steps.push_back(S); +} + +void InitializationSequence::AddAggregateParenthesizedInitStep(QualType T) { + Step S; + S.Kind = SK_AggregateParenthesizedInit; + S.Type = T; + Steps.push_back(S); +} + void InitializationSequence::RewrapReferenceInitList(QualType T, InitListExpr *Syntactic) { assert(Syntactic->getNumInits() == 1 && @@ -3932,6 +3949,255 @@ return true; } +static void TryValueInitialization(Sema &S, const InitializedEntity &Entity, + const InitializationKind &Kind, + InitializationSequence &Sequence, + InitListExpr *InitList = nullptr); + +enum AggregateParenInitKind { + APIK_Verify, + APIK_Diagnose, + APIK_Perform, +}; + +static ExprResult TryArrayParenthesizedInitialization( + Sema &S, const InitializedEntity &Entity, llvm::ArrayRef Args, + QualType DestType, QualType *ResultType, AggregateParenInitKind APIK) { + InitListExpr *Result = nullptr; + bool Invalid = false; + if (APIK == APIK_Perform) + Result = new (S.Context) InitListExpr( + S.Context, Args.front()->getBeginLoc(), None, Args.back()->getEndLoc()); + InitializedEntity IE = + InitializedEntity::InitializeElement(S.Context, 0, Entity); + + llvm::Optional NumElements; + if (DestType->isIncompleteArrayType()) { + NumElements = Args.size(); + DestType = S.Context.getConstantArrayType( + S.Context.getAsArrayType(DestType)->getElementType(), + llvm::APInt(32, *NumElements), nullptr, ArrayType::Normal, 0); + if (ResultType) { + if ((*ResultType)->isRValueReferenceType()) + *ResultType = S.Context.getRValueReferenceType(DestType); + else if ((*ResultType)->isLValueReferenceType()) + *ResultType = S.Context.getLValueReferenceType( + DestType, + (*ResultType)->castAs()->isSpelledAsLValue()); + else + *ResultType = DestType; + } + } else if (auto CAT = S.Context.getAsConstantArrayType(DestType)) { + NumElements = CAT->getSize().getZExtValue(); + } + + if (NumElements && Args.size() > *NumElements) { + // Diagnose excess initializers: int a[3](1, 2, 3, /*^*/ 4); + if (APIK == APIK_Verify) + return ExprError(); + S.Diag(Args[*NumElements]->getBeginLoc(), diag::err_excess_initializers) + << /* initKind = */ 0 /* array */ + << Args[*NumElements]->getSourceRange(); + Invalid = true; + } else if (!NumElements || Args.size() < *NumElements) { + // Ensure that we can value-initialize trailing elements. + SourceLocation Loc = Args.back()->getEndLoc().getLocWithOffset(1); + auto Kind = InitializationKind::CreateValue(Loc, Loc, Loc, true); + MultiExprArg SubInit; + IE.setElementIndex(Args.size()); + InitializationSequence ValueSequence(S, IE, Kind, SubInit); + TryValueInitialization(S, IE, Kind, ValueSequence); + if (ValueSequence.Failed()) { + if (APIK == APIK_Verify) + return ExprError(); + ValueSequence.Diagnose(S, IE, Kind, SubInit); + S.Diag(Loc, diag::note_in_omitted_aggregate_initializer) + << 0 << Args.size(); + Invalid = true; + } else if (Result) { + auto ER = ValueSequence.Perform(S, IE, Kind, SubInit); + Result->setArrayFiller(ER.get()); + } + } + + if (Result) { + Result->setType(DestType); + Result->resizeInits(S.Context, Args.size()); + } + for (unsigned Index = 0; Index != Args.size(); ++Index) { + IE.setElementIndex(Index); + Expr *Init = Args[Index]; + if (APIK == APIK_Verify) { + if (!S.CanPerformCopyInitialization(IE, Init)) + return ExprError(); + } else { + auto InitResult = + S.PerformCopyInitialization(IE, Init->getBeginLoc(), Init); + if (InitResult.isInvalid()) + Invalid = true; + else if (Result) + Result->setInit(Index, InitResult.getAs()); + } + } + + return Invalid ? ExprError() : Result; +} + +static ExprResult TryAggregateParenthesizedInitialization( + Sema &S, const InitializedEntity &Entity, llvm::ArrayRef Args, + QualType DestType, AggregateParenInitKind APIK) { + InitListExpr *Result = nullptr; + bool Invalid = false; + if (APIK == APIK_Perform) { + Result = new (S.Context) InitListExpr( + S.Context, Args.front()->getBeginLoc(), None, Args.back()->getEndLoc()); + Result->setType(DestType); + } + + // Otherwise, if no constructor is viable, the destination type is an + // aggregate class, and the initializer is a parenthesized expression-list, + // the object is initialized as follows. + RecordDecl *RD = DestType->castAs()->getDecl(); + auto Bases = + CXXRecordDecl::base_class_range(CXXRecordDecl::base_class_iterator(), + CXXRecordDecl::base_class_iterator()); + if (auto *CXXRD = dyn_cast(RD)) + Bases = CXXRD->bases(); + + unsigned Index = 0; + if (Result) + Result->resizeInits(S.Context, size(Bases)); + for (auto &Base : Bases) { + Expr *Init = Index < Args.size() ? Args[Index] : nullptr; + InitializedEntity BaseEntity = + InitializedEntity::InitializeBase(S.Context, &Base, false, &Entity); + if (Init) { + // The element e_i is copy-initialized with x_i for 1 ≤ i ≤ k. + if (Result) { + auto InitResult = + S.PerformCopyInitialization(BaseEntity, Init->getBeginLoc(), Init); + if (InitResult.isInvalid()) + Invalid = true; + else + Result->setInit(Index, InitResult.getAs()); + } else if (!S.CanPerformCopyInitialization(BaseEntity, Init)) { + if (APIK == APIK_Verify) + return ExprError(); + S.Diag(Init->getBeginLoc(), diag::note_ovl_candidate_bad_conv) + << 11 << 0 << "" << Init->getType() << BaseEntity.getType() << 0 + << Index + 1 << 0 << Init->getSourceRange(); + S.Diag(Base.getBeginLoc(), diag::note_base_class_specified_here) + << BaseEntity.getType() << Base.getSourceRange(); + Invalid = true; + } + } else { + // Bases don't have NSDMIs, so always value-init. + SourceLocation Loc = Args.back()->getEndLoc().getLocWithOffset(1); + auto Kind = InitializationKind::CreateValue(Loc, Loc, Loc, true); + MultiExprArg SubInit; + InitializationSequence ValueSequence(S, BaseEntity, Kind, SubInit); + TryValueInitialization(S, BaseEntity, Kind, ValueSequence); + if (ValueSequence.Failed()) { + if (APIK == APIK_Verify) + return ExprError(); + S.Diag(Args.back()->getEndLoc(), diag::note_ovl_candidate_arity) + << 11 << 0 << "" << 0 << Index + 1 << Args.size(); + S.Diag(Base.getBeginLoc(), diag::note_base_class_specified_here) + << BaseEntity.getType() << Base.getSourceRange(); + Invalid = true; + } else if (Result) { + auto ER = ValueSequence.Perform(S, BaseEntity, Kind, SubInit); + Result->setInit(Index, ER.get()); + } + } + ++Index; + } + + for (auto *Field : RD->fields()) { + Expr *Init = Index < Args.size() ? Args[Index] : nullptr; + if (Result) + Result->resizeInits(S.Context, Index + 1); + InitializedEntity MemberEntity = + InitializedEntity::InitializeMember(Field, &Entity); + if (Init) { + // The element e_i is copy-initialized with x_i for 1 ≤ i ≤ k. + if (Result) { + auto InitResult = S.PerformCopyInitialization( + MemberEntity, Init->getBeginLoc(), Init); + if (InitResult.isInvalid()) + Invalid = true; + else + Result->setInit(Index, InitResult.getAs()); + } else if (!S.CanPerformCopyInitialization(MemberEntity, Init)) { + if (APIK == APIK_Verify) + return ExprError(); + S.Diag(Init->getBeginLoc(), diag::note_ovl_candidate_bad_conv) + << 11 << 0 << "" << Init->getType() << MemberEntity.getType() << 0 + << Index + 1 << 0 << Init->getSourceRange(); + S.Diag(Field->getBeginLoc(), diag::note_member_declared_here) + << Field->getName() << Field->getSourceRange(); + Invalid = true; + } + } else if (Field->hasInClassInitializer()) { + // The remaining elements are initialized with their default member + // initializers, if any, + SourceLocation Loc = Args.back()->getEndLoc(); + ExprResult DIE; + if (Result) { + DIE = S.BuildCXXDefaultInitExpr(Loc, Field); + } else { + bool WasInvalid = Field->isInvalidDecl(); + Sema::TentativeAnalysisScope DisableDiag(S); + DIE = S.BuildCXXDefaultInitExpr(Loc, Field); + Field->setInvalidDecl(WasInvalid); // back out side effects + } + if (DIE.isInvalid()) { + if (APIK == APIK_Verify) + return ExprError(); + S.Diag(Args.back()->getEndLoc(), diag::note_ovl_candidate_arity) + << 11 << 0 << "" << 0 << Index + 1 << Args.size(); + S.Diag(Field->getBeginLoc(), + diag::note_init_with_default_member_initalizer) + << Field->getName() << Field->getSourceRange(); + Invalid = true; + } else if (Result) + Result->setInit(Index, DIE.get()); + } else { + // and otherwise are value-initialized. + SourceLocation Loc = Args.back()->getEndLoc().getLocWithOffset(1); + auto Kind = InitializationKind::CreateValue(Loc, Loc, Loc, true); + MultiExprArg SubInit; + InitializationSequence ValueSequence(S, MemberEntity, Kind, SubInit); + TryValueInitialization(S, MemberEntity, Kind, ValueSequence); + if (ValueSequence.Failed()) { + if (APIK == APIK_Verify) + return ExprError(); + S.Diag(Args.back()->getEndLoc(), diag::note_ovl_candidate_arity) + << 11 << 0 << "" << 0 << Index + 1 << Args.size(); + S.Diag(Field->getBeginLoc(), + diag::note_in_omitted_aggregate_initializer) + << 1 << Field << Field->getSourceRange(); + Invalid = true; + } else if (Result) { + auto ER = ValueSequence.Perform(S, MemberEntity, Kind, SubInit); + Result->setInit(Index, ER.get()); + } + } + ++Index; + } + + if (Index < Args.size()) { + if (APIK == APIK_Verify) + return ExprError(); + S.Diag(Args[Index]->getBeginLoc(), diag::note_ovl_candidate_arity) + << 11 << 0 << "" << 1 << Index << Args.size() + << Args[Index]->getSourceRange(); + Invalid = true; + } + + return Invalid ? ExprError() : Result; +} + /// Determine if the constructor has the signature of a copy or move /// constructor for the type T of the class in which it was found. That is, /// determine if its first parameter is of type T or reference to (possibly @@ -4170,6 +4436,21 @@ /*OnlyListConstructors=*/false, IsListInit); } + + // C++20 [dcl.init.general]p16.6.2.2: + // Otherwise, if no constructor is viable, the destination type is an + // aggregate class, and the initializer is a parenthesized expression-list, + // the object is initialized as follows. [...] + if (S.getLangOpts().CPlusPlus20 && Result == OR_No_Viable_Function && + DestType->isAggregateType() && + Kind.getKind() == InitializationKind::IK_Direct && + !TryAggregateParenthesizedInitialization(S, Entity, Args, DestType, + APIK_Verify) + .isInvalid()) { + Sequence.AddAggregateParenthesizedInitStep(DestType); + return; + } + if (Result) { Sequence.SetOverloadFailure( IsListInit ? InitializationSequence::FK_ListConstructorOverloadFailed @@ -4283,12 +4564,6 @@ Qualifiers T2Quals, InitializationSequence &Sequence); -static void TryValueInitialization(Sema &S, - const InitializedEntity &Entity, - const InitializationKind &Kind, - InitializationSequence &Sequence, - InitListExpr *InitList = nullptr); - /// Attempt list initialization of a reference. static void TryReferenceListInitialization(Sema &S, const InitializedEntity &Entity, @@ -5821,7 +6096,7 @@ // char16_t, an array of char32_t, or an array of wchar_t, and the // initializer is a string literal, see 8.5.2. // - Otherwise, if the destination type is an array, the program is - // ill-formed. + // ill-formed [until C++20]. if (const ArrayType *DestAT = Context.getAsArrayType(DestType)) { if (Initializer && isa(DestAT)) { SetFailed(FK_VariableLengthArrayHasInitializer); @@ -5881,6 +6156,23 @@ return; } + // C++20 permits array parenthesized initialization. + if (S.getLangOpts().CPlusPlus20 && + Kind.getKind() == InitializationKind::IK_Direct) { + if (!TryArrayParenthesizedInitialization(S, Entity, Args, DestType, + nullptr, APIK_Verify) + .isInvalid()) { + AddArrayParenthesizedInitStep(DestType); + return; + } else if (Entity.getKind() == InitializedEntity::EK_Member && + Initializer && isa(Initializer)) { + ; // fall through to GNU C++ extension below + } else { + SetFailed(FK_ArrayParenthesizedInitFailed); + return; + } + } + // Note: as an GNU C extension, we allow initialization of an // array from a compound literal that creates an array of the same // type, so long as the initializer has no side effects. @@ -8223,6 +8515,8 @@ case SK_ConstructorInitializationFromList: case SK_StdInitializerListConstructorCall: case SK_ZeroInitialization: + case SK_ArrayParenthesizedInit: + case SK_AggregateParenthesizedInit: break; } @@ -8912,6 +9206,16 @@ CurInit.get()->getValueKind()); break; } + case SK_ArrayParenthesizedInit: { + CurInit = TryArrayParenthesizedInitialization(S, Entity, Args, Step->Type, + ResultType, APIK_Perform); + break; + } + case SK_AggregateParenthesizedInit: { + CurInit = TryAggregateParenthesizedInitialization( + S, Entity, Args, Step->Type, APIK_Perform); + break; + } } } @@ -9416,6 +9720,7 @@ diag::note_previous_decl) << S.Context.getTagDeclType(Record->getDecl()); } + break; } @@ -9425,6 +9730,10 @@ S.PDiag(diag::err_ovl_no_viable_function_in_init) << DestType << ArgsRange), S, OCD_AllCandidates, Args); + + if (S.getLangOpts().CPlusPlus20 && DestType->isAggregateType() && + Kind.getKind() == InitializationKind::IK_Direct) + DiagnoseFailedAggregateParenthesizedInitialization(S, Entity, Args); break; case OR_Deleted: { @@ -9514,12 +9823,24 @@ diag::note_explicit_ctor_deduction_guide_here) << false; break; } + + case FK_ArrayParenthesizedInitFailed: { + TryArrayParenthesizedInitialization(S, Entity, Args, DestType, nullptr, + APIK_Diagnose); + break; + } } PrintInitLocationNote(S, Entity); return true; } +void InitializationSequence::DiagnoseFailedAggregateParenthesizedInitialization( + Sema &S, const InitializedEntity &Entity, ArrayRef Args) const { + TryAggregateParenthesizedInitialization(S, Entity, Args, Entity.getType(), + APIK_Diagnose); +} + void InitializationSequence::dump(raw_ostream &OS) const { switch (SequenceKind) { case FailedSequence: { @@ -9680,6 +10001,10 @@ case FK_ExplicitConstructor: OS << "list copy initialization chose explicit constructor"; break; + + case FK_ArrayParenthesizedInitFailed: + OS << "array parenthesized initialization failed"; + break; } OS << '\n'; return; @@ -9851,6 +10176,14 @@ case SK_OCLZeroOpaqueType: OS << "OpenCL opaque type from zero"; break; + + case SK_ArrayParenthesizedInit: + OS << "array initialization from parenthesized list"; + break; + + case SK_AggregateParenthesizedInit: + OS << "aggregate initialization from parenthesized list"; + break; } OS << " [" << S->Type << ']'; diff --git a/clang/test/CXX/class/class.compare/class.spaceship/p1.cpp b/clang/test/CXX/class/class.compare/class.spaceship/p1.cpp --- a/clang/test/CXX/class/class.compare/class.spaceship/p1.cpp +++ b/clang/test/CXX/class/class.compare/class.spaceship/p1.cpp @@ -3,6 +3,9 @@ namespace std { struct strong_ordering { // expected-note 6{{candidate}} int n; +#if __cpp_aggregate_paren_init >= 201902 + // expected-note@-2 2{{member n declared here}} +#endif constexpr operator int() const { return n; } static const strong_ordering less, equal, greater; }; @@ -104,6 +107,9 @@ // expected-error@#cmp {{value of type 'void' is not contextually convertible to 'bool'}} Cmp() <=> Cmp(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}G2>' first required here}}j // expected-error@#cmp {{no matching conversion for static_cast from 'void' to 'std::strong_ordering'}} +#if __cpp_aggregate_paren_init >= 201902 + // expected-note@#cmp {{candidate aggregate parenthesis initializer not viable}} +#endif Cmp() <=> Cmp(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}Cmp<{{.*}}H>' first required here}}j 0 ); @@ -135,6 +141,9 @@ // expected-error@#cmparray {{value of type 'void' is not contextually convertible to 'bool'}} CmpArray() <=> CmpArray(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}G2>' first required here}}j // expected-error@#cmparray {{no matching conversion for static_cast from 'void' to 'std::strong_ordering'}} +#if __cpp_aggregate_paren_init >= 201902 + // expected-note@#cmparray {{candidate aggregate parenthesis initializer not viable}} +#endif CmpArray() <=> CmpArray(), // expected-note-re {{in defaulted three-way comparison operator for '{{.*}}CmpArray<{{.*}}H>' first required here}}j 0 ); diff --git a/clang/test/CXX/over/over.match/over.match.viable/p3.cpp b/clang/test/CXX/over/over.match/over.match.viable/p3.cpp --- a/clang/test/CXX/over/over.match/over.match.viable/p3.cpp +++ b/clang/test/CXX/over/over.match/over.match.viable/p3.cpp @@ -25,6 +25,9 @@ (void) static_cast(S1()); (void) static_cast(S1()); // expected-error@-1 {{no matching conversion for static_cast from 'S1' to 'S2'}} +#if __cpp_aggregate_paren_init >= 201902 + // expected-note@-3 {{candidate aggregate parenthesis initializer not viable}} +#endif } // Test that constraints are checked before implicit conversions are formed. diff --git a/clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp b/clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp --- a/clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp +++ b/clang/test/CXX/temp/temp.decls/temp.variadic/p4.cpp @@ -128,6 +128,9 @@ HasMixins::HasMixins(int i): Mixins(i)... { } // expected-error@-1 {{no matching constructor for initialization of 'A'}} // expected-error@-2 {{no matching constructor for initialization of 'B'}} +#if __cpp_aggregate_paren_init >= 201902 +// expected-note@-4 2{{candidate aggregate parenthesis initializer not viable}} +#endif void test_has_mixins() { HasMixins ab; diff --git a/clang/test/Lexer/cxx-features.cpp b/clang/test/Lexer/cxx-features.cpp --- a/clang/test/Lexer/cxx-features.cpp +++ b/clang/test/Lexer/cxx-features.cpp @@ -41,8 +41,7 @@ // --- C++20 features --- -#if check(aggregate_paren_init, 0, 0, 0, 0, 0, 0) -// FIXME: 201902 in C++20 +#if check(aggregate_paren_init, 0, 0, 0, 0, 201902, 201902) #error "wrong value for __cpp_aggregate_paren_init" #endif diff --git a/clang/test/SemaCXX/cxx20-p0960-aggregate-paren-init.cpp b/clang/test/SemaCXX/cxx20-p0960-aggregate-paren-init.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/cxx20-p0960-aggregate-paren-init.cpp @@ -0,0 +1,239 @@ +// RUN: %clang_cc1 -std=c++20 -verify %s +// RUN: %clang_cc1 -std=c++17 -verify %s + +// p0960 Allow initializing aggregates from a parenthesized list of values +// p1975 Fixing the wording of parenthesized aggregate-initialization +// class.temporary/6 +// dcl.init/17.5, 17.6 + +#if __cplusplus >= 202002 && __cpp_aggregate_paren_init < 201902 +# error "should set __cpp_aggregate_paren_init in C++20 mode" +#endif + +// definitions for std::move +namespace std { template T &&move(T &); } +// definitions for std::is_constructible +namespace std { +template +inline constexpr bool is_constructible_v = __is_constructible(T, A...); +} + +int i1[](1, 2, 3); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{array initializer must be an initializer list}} +#else +static_assert(sizeof i1 == sizeof(int[3])); +#endif + +int i2[2](1, 2, 3); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{array initializer must be an initializer list}} +#else +// expected-error@-4{{excess elements in array initializer}} +#endif + +int i3[4](1, 2, 3); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{array initializer must be an initializer list}} +#endif + +int i4[](1); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{array initializer must be an initializer list}} +#else +static_assert(sizeof i4 == sizeof(int[1])); +#endif + +int *p1 = new int[](1, 2, 3); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{array 'new' cannot have initialization arguments}} +#endif + +int *p2[2] = new int[2](1, 2, 3); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{array 'new' cannot have initialization arguments}} +#else +// expected-error@-4{{excess elements in array initializer}} +#endif + +int *p3 = new int[4](1, 2, 3); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{array 'new' cannot have initialization arguments}} +#endif + +struct E { +#if __cpp_aggregate_paren_init >= 201902 +// expected-note@-2 +{{candidate constructor (the implicit copy constructor) not viable}} +// expected-note@-3 +{{candidate constructor (the implicit move constructor) not viable}} +#endif + E(int); +#if __cpp_aggregate_paren_init >= 201902 +// expected-note@-2 +{{candidate constructor not viable: requires 1 argument, but 0 were provided}} +#endif +}; + +E e1[3](1, 2, 3); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{array initializer must be an initializer list}} +#endif + +E e2[4](1, 2, 3); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{array initializer must be an initializer list}} +#else +// expected-error@-4{{no matching constructor for initialization of 'E'}} +// expected-note@-5{{in implicit initialization of array element 3 with omitted initializer}} +#endif + +struct A { +#if __cpp_aggregate_paren_init < 201902 +// expected-note@-2 +{{candidate constructor (the implicit copy constructor) not viable}} +// expected-note@-3 +{{candidate constructor (the implicit move constructor) not viable}} +// expected-note@-4 +{{candidate constructor (the implicit default constructor) not viable}} +#endif + int a; + int&& r; +}; + +int f(); +int n = 10; + +A a1{1, f()}; // OK, lifetime is extended +A a2(1, f()); // well-formed, but dangling reference +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{no matching constructor for initialization of 'A'}} +#endif +A a3{1.0, 1}; // error: narrowing conversion +// expected-error@-1{{type 'double' cannot be narrowed to 'int' in initializer list}} +// expected-note@-2{{insert an explicit cast to silence this issue}} +A a4(1.0, 1); // well-formed, but dangling reference +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{no matching constructor for initialization of 'A'}} +#endif +A a5(1.0, std::move(n)); // OK +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{no matching constructor for initialization of 'A'}} +#endif + +struct B { +// expected-note@-1 +{{candidate constructor (the implicit copy constructor) not viable}} +// expected-note@-2 +{{candidate constructor (the implicit move constructor) not viable}} +// expected-note@-3 +{{candidate constructor (the implicit default constructor) not viable}} + int a; + int b; +}; + +B b1(n); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{no matching constructor for initialization of 'B'}} +#endif +B b2(1.0); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{no matching constructor for initialization of 'B'}} +#endif +B b3(1, 2, 3); // expected-error{{no matching constructor for initialization of 'B'}} +#if __cpp_aggregate_paren_init >= 201902 +// expected-note@-2{{candidate aggregate parenthesis initializer not viable}} +#endif + +struct C { +// expected-note@-1 +{{candidate constructor (the implicit copy constructor) not viable}} +// expected-note@-2 +{{candidate constructor (the implicit move constructor) not viable}} +// expected-note@-3 +{{candidate constructor (the implicit default constructor) not viable}} + int i; + E e; +#if __cpp_aggregate_paren_init >= 201902 +// expected-note@-2{{in implicit initialization of field 'e' with omitted initializer}} +#endif + E f = 10; +}; +C c1(1); // expected-error{{no matching constructor for initialization of 'C'}} +#if __cpp_aggregate_paren_init >= 201902 +// expected-note@-2{{candidate aggregate parenthesis initializer not viable}} +#endif +C c2(1, 2); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{no matching constructor for initialization of 'C'}} +#endif + +struct D : B, E { +// expected-note@-1 +{{candidate constructor (the implicit copy constructor) not viable}} +// expected-note@-2 +{{candidate constructor (the implicit move constructor) not viable}} +// expected-note@-3 +{{candidate constructor (the implicit default constructor) not viable}} +#if __cpp_aggregate_paren_init >= 201902 +// expected-note@-5{{base class 'E' specified here}} +#endif + int i; +}; +D d1(B(1)); // expected-error{{no matching constructor for initialization of 'D'}} +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{no matching conversion for functional-style cast from 'int' to 'B'}} +#else +// expected-note@-4{{candidate aggregate parenthesis initializer not viable}} +#endif +D d2(B(), 10, 1); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2{{no matching constructor for initialization of 'D'}} +#endif + +template +struct F { +// expected-note@-1 +{{candidate constructor (the implicit copy constructor) not viable}} +// expected-note@-2 +{{candidate constructor (the implicit move constructor) not viable}} +// expected-note@-3 +{{candidate constructor (the implicit default constructor) not viable}} +// expected-note@-4 {{in instantiation of default member initializer 'F::b' requested here}} + T a; + T b = "str"; +// expected-error@-1 {{cannot initialize a member subobject of type 'int' with an lvalue of type 'const char[4]'}} +#if __cpp_aggregate_paren_init >= 201902 +// expected-note@-3 {{initializing field b with default member initializer}} +#endif +}; +F f1; +F f2("ok"); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2 {{no matching constructor for initialization of 'F'}} +#endif +F f3("ok", "ok"); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2 {{no matching constructor for initialization of 'F'}} +#endif +F f4; // expected-note {{in evaluation of exception specification for 'F::F' needed here}} +F f5(10); // expected-error {{no matching constructor for initialization of 'F'}} +#if __cpp_aggregate_paren_init >= 201902 +// expected-note@-2{{candidate aggregate parenthesis initializer not viable}} +#endif +F f6(10, 10); +#if __cpp_aggregate_paren_init < 201902 +// expected-error@-2 {{no matching constructor for initialization of 'F'}} +#endif + +#ifndef __cpp_aggregate_paren_init +# define __cpp_aggregate_paren_init 0 +#endif + +static_assert(std::is_constructible_v); +static_assert(std::is_constructible_v == (__cpp_aggregate_paren_init >= 201902)); +static_assert(std::is_constructible_v == (__cpp_aggregate_paren_init >= 201902)); +static_assert(std::is_constructible_v == (__cpp_aggregate_paren_init >= 201902)); +static_assert(std::is_constructible_v == (__cpp_aggregate_paren_init >= 201902)); +static_assert(not std::is_constructible_v); +static_assert(not std::is_constructible_v); +static_assert(not std::is_constructible_v); +static_assert(not std::is_constructible_v); +static_assert(std::is_constructible_v == (__cpp_aggregate_paren_init >= 201902)); +static_assert(std::is_constructible_v == (__cpp_aggregate_paren_init >= 201902)); +static_assert(not std::is_constructible_v); +static_assert(std::is_constructible_v == (__cpp_aggregate_paren_init >= 201902)); +static_assert(std::is_constructible_v, char const*> == (__cpp_aggregate_paren_init >= 201902)); +static_assert(not std::is_constructible_v, int>); +static_assert(std::is_constructible_v, int, int> == (__cpp_aggregate_paren_init >= 201902)); + +static_assert(std::is_constructible_v); +static_assert(std::is_constructible_v == (__cpp_aggregate_paren_init >= 201902)); +static_assert(std::is_constructible_v == (__cpp_aggregate_paren_init >= 201902)); +static_assert(not std::is_constructible_v); +static_assert(not std::is_constructible_v); +static_assert(std::is_constructible_v == (__cpp_aggregate_paren_init >= 201902)); +static_assert(not std::is_constructible_v); +static_assert(std::is_constructible_v == (__cpp_aggregate_paren_init >= 201902)); diff --git a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp --- a/clang/test/SemaCXX/cxx2a-explicit-bool.cpp +++ b/clang/test/SemaCXX/cxx2a-explicit-bool.cpp @@ -30,6 +30,9 @@ A<-1> a(0); // expected-error@-1 {{no matching constructor}} // expected-note@-2 {{in instantiation of template class}} +#if __cpp_aggregate_paren_init >= 201902 +// expected-note@-4{{candidate aggregate parenthesis initializer not viable}} +#endif template struct B {